/*
XXX  Fixme : shouldnt show [malformed frame] for long packets
*/

/* packet-smb-pipe.c
 * Routines for SMB named pipe packet dissection
 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
 * significant rewrite to tvbuffify the dissector, Ronnie Sahlberg and
 * Guy Harris 2001
 *
 * $Id: packet-smb-pipe.c,v 1.68 2002/01/27 22:25:48 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * Copyright 1998 Gerald Combs
 *
 * Copied from packet-pop.c
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#include <time.h>
#include <string.h>
#include <glib.h>
#include <ctype.h>
#include <epan/packet.h>
#include <epan/conversation.h>
#include "smb.h"
#include "packet-smb-pipe.h"
#include "packet-smb-browse.h"
#include "packet-dcerpc.h"
#include "reassemble.h"

static int proto_smb_pipe = -1;
static int hf_pipe_function = -1;
static int hf_pipe_priority = -1;
static int hf_pipe_peek_available = -1;
static int hf_pipe_peek_remaining = -1;
static int hf_pipe_peek_status = -1;
static int hf_pipe_getinfo_info_level = -1;
static int hf_pipe_getinfo_output_buffer_size = -1;
static int hf_pipe_getinfo_input_buffer_size = -1;
static int hf_pipe_getinfo_maximum_instances = -1;
static int hf_pipe_getinfo_current_instances = -1;
static int hf_pipe_getinfo_pipe_name_length = -1;
static int hf_pipe_getinfo_pipe_name = -1;
static int hf_pipe_write_raw_bytes_written = -1;

static gint ett_smb_pipe = -1;
static gint ett_smb_pipe_fragments = -1;

static int proto_smb_lanman = -1;
static int hf_function_code = -1;
static int hf_param_desc = -1;
static int hf_return_desc = -1;
static int hf_aux_data_desc = -1;
static int hf_detail_level = -1;
static int hf_recv_buf_len = -1;
static int hf_send_buf_len = -1;
static int hf_continuation_from = -1;
static int hf_status = -1;
static int hf_convert = -1;
static int hf_ecount = -1;
static int hf_acount = -1;
static int hf_share_name = -1;
static int hf_share_type = -1;
static int hf_share_comment = -1;
static int hf_share_permissions = -1;
static int hf_share_max_uses = -1;
static int hf_share_current_uses = -1;
static int hf_share_path = -1;
static int hf_share_password = -1;
static int hf_server_name = -1;
static int hf_server_major = -1;
static int hf_server_minor = -1;
static int hf_server_comment = -1;
static int hf_abytes = -1;
static int hf_current_time = -1;
static int hf_msecs = -1;
static int hf_hour = -1;
static int hf_minute = -1;
static int hf_second = -1;
static int hf_hundredths = -1;
static int hf_tzoffset = -1;
static int hf_timeinterval = -1;
static int hf_day = -1;
static int hf_month = -1;
static int hf_year = -1;
static int hf_weekday = -1;
static int hf_enumeration_domain = -1;
static int hf_computer_name = -1;
static int hf_user_name = -1;
static int hf_group_name = -1;
static int hf_workstation_domain = -1;
static int hf_workstation_major = -1;
static int hf_workstation_minor = -1;
static int hf_logon_domain = -1;
static int hf_other_domains = -1;
static int hf_password = -1;
static int hf_workstation_name = -1;
static int hf_ustruct_size = -1;
static int hf_logon_code = -1;
static int hf_privilege_level = -1;
static int hf_operator_privileges = -1;
static int hf_num_logons = -1;
static int hf_bad_pw_count = -1;
static int hf_last_logon = -1;
static int hf_last_logoff = -1;
static int hf_logoff_time = -1;
static int hf_kickoff_time = -1;
static int hf_password_age = -1;
static int hf_password_can_change = -1;
static int hf_password_must_change = -1;
static int hf_script_path = -1;
static int hf_logoff_code = -1;
static int hf_duration = -1;
static int hf_comment = -1;
static int hf_user_comment = -1;
static int hf_full_name = -1;
static int hf_homedir = -1;
static int hf_parameters = -1;
static int hf_logon_server = -1;
static int hf_country_code = -1;
static int hf_workstations = -1;
static int hf_max_storage = -1;
static int hf_units_per_week = -1;
static int hf_logon_hours = -1;
static int hf_code_page = -1;
static int hf_new_password = -1;
static int hf_old_password = -1;
static int hf_reserved = -1;

static gint ett_lanman = -1;
static gint ett_lanman_unknown_entries = -1;
static gint ett_lanman_unknown_entry = -1;
static gint ett_lanman_shares = -1;
static gint ett_lanman_share = -1;
static gint ett_lanman_groups = -1;
static gint ett_lanman_servers = -1;
static gint ett_lanman_server = -1;

static dissector_handle_t data_handle;

/*
 * See
 *
 *	ftp://ftp.microsoft.com/developr/drg/CIFS/cifsrap2.txt
 *
 * among other documents.
 */

static const value_string status_vals[] = {
	{0,	"Success"},
	{5,	"User has insufficient privilege"},
	{65,	"Network access is denied"},
	{86,	"The specified password is invalid"},
	{SMBE_moredata, "Additional data is available"},
	{2114,	"Service is not running on the remote computer"},
	{2123,	"Supplied buffer is too small"},
	{2141,	"Server is not configured for transactions (IPC$ not shared)"},
	{2212,  "An error occurred while loading or running the logon script"},
	{2214,  "The logon was not validated by any server"},
	{2217,  "The logon server is running an older software version"},
	{2221,  "The user name was not found"},
	{2240,  "The user is not allowed to logon from this computer"},
	{2241,  "The user is not allowed to logon at this time"},
	{2242,  "The user password has expired"},
	{2243,  "The password cannot be changed"},
	{2246,  "The password is too short"},
	{0,     NULL}
};

static const value_string share_type_vals[] = {
	{0, "Directory tree"},
	{1, "Printer queue"},
	{2, "Communications device"},
	{3, "IPC"},
	{0, NULL}
};

static const value_string privilege_vals[] = {
	{0, "Guest"},
	{1, "User"},
	{2, "Administrator"},
	{0, NULL}
};

static const value_string op_privilege_vals[] = {
	{0, "Print operator"},
	{1, "Communications operator"},
	{2, "Server operator"},
	{3, "Accounts operator"},
	{0, NULL}
};

static const value_string weekday_vals[] = {
	{0, "Sunday"},
	{1, "Monday"},
	{2, "Tuesday"},
	{3, "Wednesday"},
	{4, "Thursday"},
	{5, "Friday"},
	{6, "Saturday"},
	{0, NULL}
};

static int
add_word_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint16 WParam;

	if (hf_index != -1)
		proto_tree_add_item(tree, hf_index, tvb, offset, 2, TRUE);
	else {
		WParam = tvb_get_letohs(tvb, offset);
		proto_tree_add_text(tree, tvb, offset, 2,
		    "Word Param: %u (0x%04X)", WParam, WParam);
	}
	offset += 2;
	return offset;
}

static int
add_dword_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint32 LParam;

	if (hf_index != -1)
		proto_tree_add_item(tree, hf_index, tvb, offset, 4, TRUE);
	else {
		LParam = tvb_get_letohl(tvb, offset);
		proto_tree_add_text(tree, tvb, offset, 4,
		    "Doubleword Param: %u (0x%08X)", LParam, LParam);
	}
	offset += 4;
	return offset;
}

static int
add_byte_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint8 BParam;

	if (hf_index != -1)
		proto_tree_add_item(tree, hf_index, tvb, offset, count, TRUE);
	else {
		if (count == 1) {
			BParam = tvb_get_guint8(tvb, offset);
			proto_tree_add_text(tree, tvb, offset, count,
			    "Byte Param: %u (0x%02X)",
			    BParam, BParam);
		} else {
			proto_tree_add_text(tree, tvb, offset, count,
			    "Byte Param: %s",
			    tvb_bytes_to_str(tvb, offset, count));
		}
	}
	offset += count;
	return offset;
}

static int
add_pad_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	/*
	 * This is for parameters that have descriptor entries but that
	 * are, in practice, just padding.
	 */
	offset += count;
	return offset;
}

static void
add_null_pointer_param(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	if (hf_index != -1) {
		proto_tree_add_text(tree, tvb, offset, 0,
		  "%s (Null pointer)",
		  proto_registrar_get_name(hf_index));
	} else {
		proto_tree_add_text(tree, tvb, offset, 0,
		    "String Param (Null pointer)");
	}
}

static int
add_string_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint string_len;

	string_len = tvb_strsize(tvb, offset);
	if (hf_index != -1) {
		proto_tree_add_item(tree, hf_index, tvb, offset, string_len,
		    TRUE);
	} else {
		proto_tree_add_text(tree, tvb, offset, string_len,
		    "String Param: %s",
		    tvb_format_text(tvb, offset, string_len));
	}
	offset += string_len;
	return offset;
}

static const char *
get_stringz_pointer_value(tvbuff_t *tvb, int offset, int convert, int *cptrp,
    int *lenp)
{
	int cptr;
	gint string_len;

	/* pointer to string */
	cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
	*cptrp = cptr;

	/* string */
	if (tvb_offset_exists(tvb, cptr) &&
	    (string_len = tvb_strnlen(tvb, cptr, -1)) != -1) {
	    	string_len++;	/* include the terminating '\0' */
	    	*lenp = string_len;
	    	return tvb_format_text(tvb, cptr, string_len - 1);
	} else
		return NULL;
}

static int
add_stringz_pointer_param(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	int cptr;
	const char *string;
	gint string_len;

	string = get_stringz_pointer_value(tvb, offset, convert, &cptr,
	    &string_len);
	offset += 4;

	/* string */
	if (string != NULL) {
		if (hf_index != -1) {
			proto_tree_add_item(tree, hf_index, tvb, cptr,
			    string_len, TRUE);
		} else {
			proto_tree_add_text(tree, tvb, cptr, string_len,
			    "String Param: %s", string);
		}
	} else {
		if (hf_index != -1) {
			proto_tree_add_text(tree, tvb, 0, 0,
			    "%s: <String goes past end of frame>",
			    proto_registrar_get_name(hf_index));
		} else {
			proto_tree_add_text(tree, tvb, 0, 0,
			    "String Param: <String goes past end of frame>");
		}
	}

	return offset;
}

static int
add_bytes_pointer_param(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	int cptr;

	/* pointer to byte array */
	cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
	offset += 4;

	/* bytes */
	if (tvb_bytes_exist(tvb, cptr, count)) {
		if (hf_index != -1) {
			proto_tree_add_item(tree, hf_index, tvb, cptr,
			    count, TRUE);
		} else {
			proto_tree_add_text(tree, tvb, cptr, count,
			    "Byte Param: %s",
			    tvb_bytes_to_str(tvb, cptr, count));
		}
	} else {
		if (hf_index != -1) {
			proto_tree_add_text(tree, tvb, 0, 0,
			    "%s: <Bytes go past end of frame>",
			    proto_registrar_get_name(hf_index));
		} else {
			proto_tree_add_text(tree, tvb, 0, 0,
			    "Byte Param: <Bytes goes past end of frame>");
		}
	}

	return offset;
}

static int
add_detail_level(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	struct smb_info *smb_info = pinfo->private_data;
	smb_transact_info_t *trp = smb_info->sip->extra_info;
	guint16 level;

	level = tvb_get_letohs(tvb, offset);
	if (!pinfo->fd->flags.visited)
		trp->info_level = level;	/* remember this for the response */
	proto_tree_add_uint(tree, hf_index, tvb, offset, 2, level);
	offset += 2;
	return offset;
}

static int
add_max_uses(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint16 WParam;

	WParam = tvb_get_letohs(tvb, offset);
	if (WParam == 0xffff) {	/* -1 */
		proto_tree_add_uint_format(tree, hf_index, tvb,
		    offset, 2, WParam,
		    "%s: No limit",
		    proto_registrar_get_name(hf_index));
	} else {
		proto_tree_add_uint(tree, hf_index, tvb,
			    offset, 2, WParam);
	}
	offset += 2;
	return offset;
}

static int
add_server_type(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	dissect_smb_server_type_flags(tvb, pinfo, tree, offset, FALSE);
	offset += 4;
	return offset;
}

static int
add_server_type_info(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	dissect_smb_server_type_flags(tvb, pinfo, tree, offset, TRUE);
	offset += 4;
	return offset;
}

static int
add_reltime(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	nstime_t nstime;

	nstime.secs = tvb_get_letohl(tvb, offset);
	nstime.nsecs = 0;
	proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
	    &nstime, "%s: %s", proto_registrar_get_name(hf_index),
	    time_secs_to_str(nstime.secs));
	offset += 4;
	return offset;
}

/*
 * Sigh.  These are for handling Microsoft's annoying almost-UNIX-time-but-
 * it's-local-time-not-UTC time.
 */
static int
add_abstime_common(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index,
    const char *absent_name)
{
	nstime_t nstime;
	struct tm *tmp;

	nstime.secs = tvb_get_letohl(tvb, offset);
	nstime.nsecs = 0;
	/*
	 * Sigh.  Sometimes it appears that -1 means "unknown", and
	 * sometimes it appears that 0 means "unknown", for the last
	 * logoff date/time.
	 */
	if (nstime.secs == -1 || nstime.secs == 0) {
		proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
		    &nstime, "%s: %s", proto_registrar_get_name(hf_index),
		    absent_name);
	} else {
		/*
		 * Run it through "gmtime()" to break it down, and then
		 * run it through "mktime()" to put it back together
		 * as UTC.
		 */
		tmp = gmtime(&nstime.secs);
		tmp->tm_isdst = -1;	/* we don't know if it's DST or not */
		nstime.secs = mktime(tmp);
		proto_tree_add_time(tree, hf_index, tvb, offset, 4,
		    &nstime);
	}
	offset += 4;
	return offset;
}

static int
add_abstime_absent_never(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	return add_abstime_common(tvb, offset, count, pinfo, tree,
	    convert, hf_index, "Never");
}

static int
add_abstime_absent_unknown(tvbuff_t *tvb, int offset, int count,
    packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
	return add_abstime_common(tvb, offset, count, pinfo, tree,
	    convert, hf_index, "Unknown");
}

static int
add_nlogons(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint16 nlogons;

	nlogons = tvb_get_letohs(tvb, offset);
	if (nlogons == 0xffff)	/* -1 */
		proto_tree_add_uint_format(tree, hf_index, tvb, offset, 2,
		    nlogons, "%s: Unknown",
		    proto_registrar_get_name(hf_index));
	else
		proto_tree_add_uint(tree, hf_index, tvb, offset, 2,
		    nlogons);
	offset += 2;
	return offset;
}

static int
add_max_storage(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint32 max_storage;

	max_storage = tvb_get_letohl(tvb, offset);
	if (max_storage == 0xffffffff)
		proto_tree_add_uint_format(tree, hf_index, tvb, offset, 4,
		    max_storage, "%s: No limit",
		    proto_registrar_get_name(hf_index));
	else
		proto_tree_add_uint(tree, hf_index, tvb, offset, 4,
		    max_storage);
	offset += 4;
	return offset;
}

static int
add_logon_hours(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	int cptr;

	/* pointer to byte array */
	cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
	offset += 4;

	/* bytes */
	if (tvb_bytes_exist(tvb, cptr, count)) {
		if (count == 21) {
			/*
			 * The logon hours should be exactly 21 bytes long.
			 *
			 * XXX - should actually carve up the bits;
			 * we need the units per week to do that, though.
			 */
			proto_tree_add_item(tree, hf_index, tvb, cptr, count,
			    TRUE);
		} else {
			proto_tree_add_bytes_format(tree, hf_index, tvb,
			    cptr, count, tvb_get_ptr(tvb, cptr, count),
			    "%s: %s (wrong length, should be 21, is %d",
			    proto_registrar_get_name(hf_index),
			    tvb_bytes_to_str(tvb, cptr, count), count);
		}
	} else {
		proto_tree_add_text(tree, tvb, 0, 0,
		    "%s: <Bytes go past end of frame>",
		    proto_registrar_get_name(hf_index));
	}

	return offset;
}

static int
add_tzoffset(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	gint16 tzoffset;

	tzoffset = tvb_get_letohs(tvb, offset);
	if (tzoffset < 0) {
		proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
		    tzoffset, "%s: %s east of UTC",
		    proto_registrar_get_name(hf_index),
		    time_secs_to_str(-tzoffset*60));
	} else if (tzoffset > 0) {
		proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
		    tzoffset, "%s: %s west of UTC",
		    proto_registrar_get_name(hf_index),
		    time_secs_to_str(tzoffset*60));
	} else {
		proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
		    tzoffset, "%s: at UTC",
		    proto_registrar_get_name(hf_index));
	}
	offset += 2;
	return offset;
}

static int
add_timeinterval(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	guint16 timeinterval;

	timeinterval = tvb_get_letohs(tvb, offset);
	proto_tree_add_uint_format(tree, hf_timeinterval, tvb, offset, 2,
	   timeinterval, "%s: %f seconds", proto_registrar_get_name(hf_index),
	   timeinterval*.0001);
	offset += 2;
	return offset;
}

static int
add_logon_args(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
    proto_tree *tree, int convert, int hf_index)
{
	if (count != 54) {
		proto_tree_add_text(tree, tvb, offset, count,
		   "Bogus NetWkstaUserLogon parameters: length is %d, should be 54",
		   count);
		offset += count;
		return offset;
	}

	/* user name */
	proto_tree_add_item(tree, hf_user_name, tvb, offset, 21, TRUE);
	offset += 21;

	/* pad1 */
	offset += 1;

	/* password */
	proto_tree_add_item(tree, hf_password, tvb, offset, 15, TRUE);
	offset += 15;

	/* pad2 */
	offset += 1;

	/* workstation name */
	proto_tree_add_item(tree, hf_workstation_name, tvb, offset, 16, TRUE);
	offset += 16;
	return offset;
}

/* 
 * The following data structure describes the Remote API requests we
 * understand.
 *
 * Simply fill in the number and parameter information.
 * Try to keep them in order.
 *
 * We will extend this data structure as we try to decode more.
 */

/*
 * This is a pointer to a function to process an item.
 */
typedef int	(*item_func)(tvbuff_t *, int, int, packet_info *, proto_tree *,
			     int, int);

/*
 * Type of an item; determines what parameter strings are valid for
 * the item.
 */
typedef enum {
	PARAM_NONE,	/* for the end-of-list stopper */
	PARAM_WORD,	/* 'W' or 'h' - 16-bit word */
	PARAM_DWORD,	/* 'D' or 'i' - 32-bit word */
	PARAM_BYTES,	/* 'B' or 'b' or 'g' or 'O' - one or more bytes */
	PARAM_STRINGZ,	/* 'z' or 'O' - null-terminated string */
} param_type_t;

/*
 * This structure describes an item; "hf_index" points to the index
 * for the field corresponding to that item, "func" points to the
 * function to use to add that item to the tree, and "type" is the
 * type that the item is supposed to have.
 */
typedef struct {
	int		*hf_index;
	item_func	func;
	param_type_t	type;
} item_t;

/*
 * This structure describes a list of items; each list of items
 * has a corresponding detail level.
 */
typedef struct {
	int		level;
	const item_t	*item_list;
} item_list_t;

struct lanman_desc {
	int		lanman_num;
	const item_t	*req;
	proto_item	*(*req_data_item)(tvbuff_t *, packet_info *,
					  proto_tree *, int);
	gint		*ett_req_data;
	const item_t	*req_data;
	const item_t	*req_aux_data;
	const item_t	*resp;
	const gchar	*resp_data_entry_list_label;
	gint		*ett_data_entry_list;
	proto_item	*(*resp_data_element_item)(tvbuff_t *, packet_info *,
						   proto_tree *, int);
	gint		*ett_resp_data_element_item;
	const item_list_t *resp_data_list;
	const item_t	*resp_aux_data;
};

static int no_hf = -1;	/* for padding crap */

static const item_t lm_params_req_netshareenum[] = {
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ &hf_recv_buf_len, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netshareenum[] = {
	{ &hf_acount, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

/*
 * Create a subtree for a share.
 */
static proto_item *
netshareenum_share_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    int offset)
{
	if (tree) {
		return proto_tree_add_text(tree, tvb, offset, -1,
		    "Share %.13s", tvb_get_ptr(tvb, offset, 13));
	} else
		return NULL;
}

static const item_t lm_null[] = {
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_null_list[] = {
	{ -1, lm_null }
};

static const item_t lm_data_resp_netshareenum_1[] = {
	{ &hf_share_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_share_type, add_word_param, PARAM_WORD },
	{ &hf_share_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netshareenum[] = {
	{ 1, lm_data_resp_netshareenum_1 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netsharegetinfo[] = {
	{ &hf_share_name, add_string_param, PARAM_STRINGZ },
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netsharegetinfo[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netsharegetinfo_0[] = {
	{ &hf_share_name, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netsharegetinfo_1[] = {
	{ &hf_share_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_share_type, add_word_param, PARAM_WORD },
	{ &hf_share_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netsharegetinfo_2[] = {
	{ &hf_share_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_share_type, add_word_param, PARAM_WORD },
	{ &hf_share_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_share_permissions, add_word_param, PARAM_WORD }, /* XXX - do as bit fields */
	{ &hf_share_max_uses, add_max_uses, PARAM_WORD },
	{ &hf_share_current_uses, add_word_param, PARAM_WORD },
	{ &hf_share_path, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_share_password, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netsharegetinfo[] = {
	{ 0, lm_data_resp_netsharegetinfo_0 },
	{ 1, lm_data_resp_netsharegetinfo_1 },
	{ 2, lm_data_resp_netsharegetinfo_2 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netservergetinfo[] = {
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netservergetinfo[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_serverinfo_0[] = {
	{ &hf_server_name, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_serverinfo_1[] = {
	{ &hf_server_name, add_byte_param, PARAM_BYTES },
	{ &hf_server_major, add_byte_param, PARAM_BYTES },
	{ &hf_server_minor, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_server_type, PARAM_DWORD },
	{ &hf_server_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_serverinfo[] = {
	{ 0, lm_data_serverinfo_0 },
	{ 1, lm_data_serverinfo_1 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netusergetinfo[] = {
	{ &hf_user_name, add_string_param, PARAM_STRINGZ },
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netusergetinfo[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netusergetinfo_11[] = {
	{ &hf_user_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_user_comment, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_full_name, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_privilege_level, add_word_param, PARAM_WORD },
	{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
	{ &hf_password_age, add_reltime, PARAM_DWORD },
	{ &hf_homedir, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_parameters, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
	{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
	{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
	{ &hf_num_logons, add_nlogons, PARAM_WORD },
	{ &hf_logon_server, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_country_code, add_word_param, PARAM_WORD },
	{ &hf_workstations, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_max_storage, add_max_storage, PARAM_DWORD },
	{ &hf_units_per_week, add_word_param, PARAM_WORD },
	{ &hf_logon_hours, add_logon_hours, PARAM_BYTES },
	{ &hf_code_page, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netusergetinfo[] = {
	{ 11, lm_data_resp_netusergetinfo_11 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netusergetgroups[] = {
	{ &hf_user_name, add_string_param, PARAM_STRINGZ },
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netusergetgroups[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netusergetgroups_0[] = {
	{ &hf_group_name, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netusergetgroups[] = {
	{ 0, lm_data_resp_netusergetgroups_0 },
	{ -1, lm_null }
};

/*
 * Has no detail level; make it the default.
 */
static const item_t lm_data_resp_netremotetod_nolevel[] = {
	{ &hf_current_time, add_abstime_absent_unknown, PARAM_DWORD },
	{ &hf_msecs, add_dword_param, PARAM_DWORD },
	{ &hf_hour, add_byte_param, PARAM_BYTES },
	{ &hf_minute, add_byte_param, PARAM_BYTES },
	{ &hf_second, add_byte_param, PARAM_BYTES },
	{ &hf_hundredths, add_byte_param, PARAM_BYTES },
	{ &hf_tzoffset, add_tzoffset, PARAM_WORD },
	{ &hf_timeinterval, add_timeinterval, PARAM_WORD },
	{ &hf_day, add_byte_param, PARAM_BYTES },
	{ &hf_month, add_byte_param, PARAM_BYTES },
	{ &hf_year, add_word_param, PARAM_WORD },
	{ &hf_weekday, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netremotetod[] = {
	{ -1, lm_data_resp_netremotetod_nolevel },
};

static const item_t lm_params_req_netserverenum2[] = {
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ &no_hf, add_server_type_info, PARAM_DWORD },
	{ &hf_enumeration_domain, add_string_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

/*
 * Create a subtree for a server.
 */
static proto_item *
netserverenum2_server_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    int offset)
{
	if (tree) {
		return proto_tree_add_text(tree, tvb, offset, -1,
			    "Server %.16s", tvb_get_ptr(tvb, offset, 16));
	} else
		return NULL;
}

static const item_t lm_params_resp_netserverenum2[] = {
	{ &hf_acount, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_req_netwkstagetinfo[] = {
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netwkstagetinfo[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netwkstagetinfo_10[] = {
	{ &hf_computer_name, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_user_name, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_workstation_domain, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_workstation_major, add_byte_param, PARAM_BYTES },
	{ &hf_workstation_minor, add_byte_param, PARAM_BYTES },
	{ &hf_logon_domain, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_other_domains, add_stringz_pointer_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netwkstagetinfo[] = {
	{ 10, lm_data_resp_netwkstagetinfo_10 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netwkstauserlogon[] = {
	{ &no_hf, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &no_hf, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_detail_level, add_detail_level, PARAM_WORD },
	{ &no_hf, add_logon_args, PARAM_BYTES },
	{ &hf_ustruct_size, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netwkstauserlogon[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netwkstauserlogon_1[] = {
	{ &hf_logon_code, add_word_param, PARAM_WORD },
	{ &hf_user_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_privilege_level, add_word_param, PARAM_WORD },
	{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
	{ &hf_num_logons, add_nlogons, PARAM_WORD },
	{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
	{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
	{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
	{ &hf_logoff_time, add_abstime_absent_never, PARAM_DWORD },
	{ &hf_kickoff_time, add_abstime_absent_never, PARAM_DWORD },
	{ &hf_password_age, add_reltime, PARAM_DWORD },
	{ &hf_password_can_change, add_abstime_absent_never, PARAM_DWORD },
	{ &hf_password_must_change, add_abstime_absent_never, PARAM_DWORD },
	{ &hf_server_name, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_logon_domain, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_script_path, add_stringz_pointer_param, PARAM_STRINGZ },
	{ &hf_reserved, add_dword_param, PARAM_DWORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netwkstauserlogon[] = {
	{ 1, lm_data_resp_netwkstauserlogon_1 },
	{ -1, lm_null }
};

static const item_t lm_params_req_netwkstauserlogoff[] = {
	{ &hf_user_name, add_byte_param, PARAM_BYTES },
	{ &no_hf, add_pad_param, PARAM_BYTES },
	{ &hf_workstation_name, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_params_resp_netwkstauserlogoff[] = {
	{ &hf_abytes, add_word_param, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_resp_netwkstauserlogoff_1[] = {
	{ &hf_logoff_code, add_word_param, PARAM_WORD },
	{ &hf_duration, add_reltime, PARAM_DWORD },
	{ &hf_num_logons, add_nlogons, PARAM_WORD },
	{ NULL, NULL, PARAM_NONE }
};

static const item_list_t lm_data_resp_netwkstauserlogoff[] = {
	{ 1, lm_data_resp_netwkstauserlogoff_1 },
	{ -1, lm_null }
};

static const item_t lm_params_req_samoemchangepassword[] = {
	{ &hf_user_name, add_string_param, PARAM_STRINGZ },
	{ NULL, NULL, PARAM_NONE }
};

static const item_t lm_data_req_samoemchangepassword[] = {
	{ &hf_new_password, add_byte_param, PARAM_BYTES },
	{ &hf_old_password, add_byte_param, PARAM_BYTES },
	{ NULL, NULL, PARAM_NONE }
};

/*
 * Create a subtree for an entry we don't yet know how to dissect.
 */
static proto_item *
unknown_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    int offset)
{
	if (tree) {
		return proto_tree_add_text(tree, tvb, offset, -1,
			    "Unknown entry");
	} else
		return NULL;
}

#define API_NetShareEnum		0
#define API_NetShareGetInfo		1
#define API_NetShareSetInfo		2
#define API_NetShareAdd			3
#define API_NetShareDel			4
#define API_NetShareCheck		5
#define API_NetSessionEnum		6
#define API_NetSessionGetInfo		7
#define API_NetSessionDel		8
#define API_WconnectionEnum		9
#define API_NetFileEnum			10
#define API_NetFileGetInfo		11
#define API_NetFileClose		12
#define API_NetServerGetInfo		13
#define API_NetServerSetInfo		14
#define API_NetServerDiskEnum		15
#define API_NetServerAdminCommand	16
#define API_NetAuditOpen		17
#define API_NetAuditClear		18
#define API_NetErrorLogOpen		19
#define API_NetErrorLogClear		20
#define API_NetCharDevEnum		21
#define API_NetCharDevGetInfo		22
#define API_NetCharDevControl		23
#define API_NetCharDevQEnum		24
#define API_NetCharDevQGetInfo		25
#define API_NetCharDevQSetInfo		26
#define API_NetCharDevQPurge		27
#define API_NetCharDevQPurgeSelf	28
#define API_NetMessageNameEnum		29
#define API_NetMessageNameGetInfo	30
#define API_NetMessageNameAdd		31
#define API_NetMessageNameDel		32
#define API_NetMessageNameFwd		33
#define API_NetMessageNameUnFwd		34
#define API_NetMessageBufferSend	35
#define API_NetMessageFileSend		36
#define API_NetMessageLogFileSet	37
#define API_NetMessageLogFileGet	38
#define API_NetServiceEnum		39
#define API_NetServiceInstall		40
#define API_NetServiceControl		41
#define API_NetAccessEnum		42
#define API_NetAccessGetInfo		43
#define API_NetAccessSetInfo		44
#define API_NetAccessAdd		45
#define API_NetAccessDel		46
#define API_NetGroupEnum		47
#define API_NetGroupAdd			48
#define API_NetGroupDel			49
#define API_NetGroupAddUser		50
#define API_NetGroupDelUser		51
#define API_NetGroupGetUsers		52
#define API_NetUserEnum			53
#define API_NetUserAdd			54
#define API_NetUserDel			55
#define API_NetUserGetInfo		56
#define API_NetUserSetInfo		57
#define API_NetUserPasswordSet		58
#define API_NetUserGetGroups		59
/*This line and number replaced a Dead Entry for 60 */
/*This line and number replaced a Dead Entry for 61 */
#define API_NetWkstaSetUID		62
#define API_NetWkstaGetInfo		63
#define API_NetWkstaSetInfo		64
#define API_NetUseEnum			65
#define API_NetUseAdd			66
#define API_NetUseDel			67
#define API_NetUseGetInfo		68
#define API_WPrintQEnum			69
#define API_WPrintQGetInfo		70
#define API_WPrintQSetInfo		71
#define API_WPrintQAdd			72
#define API_WPrintQDel			73
#define API_WPrintQPause		74
#define API_WPrintQContinue		75
#define API_WPrintJobEnum		76
#define API_WPrintJobGetInfo		77
#define API_WPrintJobSetInfo_OLD	78
/* This line and number replaced a Dead Entry for 79 */
/* This line and number replaced a Dead Entry for 80 */
#define API_WPrintJobDel		81
#define API_WPrintJobPause		82
#define API_WPrintJobContinue		83
#define API_WPrintDestEnum		84
#define API_WPrintDestGetInfo		85
#define API_WPrintDestControl		86
#define API_NetProfileSave		87
#define API_NetProfileLoad		88
#define API_NetStatisticsGet		89
#define API_NetStatisticsClear		90
#define API_NetRemoteTOD		91
#define API_WNetBiosEnum		92
#define API_WNetBiosGetInfo		93
#define API_NetServerEnum		94
#define API_I_NetServerEnum		95
#define API_NetServiceGetInfo		96
/* This line and number replaced a Dead Entry for 97 */
/* This line and number replaced a Dead Entry for 98 */
/* This line and number replaced a Dead Entry for 99 */
/* This line and number replaced a Dead Entry for 100 */
/* This line and number replaced a Dead Entry for 101 */
/* This line and number replaced a Dead Entry for 102 */
#define API_WPrintQPurge		103
#define API_NetServerEnum2		104
#define API_NetAccessGetUserPerms	105
#define API_NetGroupGetInfo		106
#define API_NetGroupSetInfo		107
#define API_NetGroupSetUsers		108
#define API_NetUserSetGroups		109
#define API_NetUserModalsGet		110
#define API_NetUserModalsSet		111
#define API_NetFileEnum2		112
#define API_NetUserAdd2			113
#define API_NetUserSetInfo2		114
#define API_NetUserPasswordSet2		115
#define API_I_NetServerEnum2		116
#define API_NetConfigGet2		117
#define API_NetConfigGetAll2		118
#define API_NetGetDCName		119
#define API_NetHandleGetInfo		120
#define API_NetHandleSetInfo		121
#define API_NetStatisticsGet2		122
#define API_WBuildGetInfo		123
#define API_NetFileGetInfo2		124
#define API_NetFileClose2		125
#define API_NetServerReqChallenge	126
#define API_NetServerAuthenticate	127
#define API_NetServerPasswordSet	128
#define API_WNetAccountDeltas		129
#define API_WNetAccountSync		130
#define API_NetUserEnum2		131
#define API_NetWkstaUserLogon		132
#define API_NetWkstaUserLogoff		133
#define API_NetLogonEnum		134
#define API_NetErrorLogRead		135
#define API_I_NetPathType		136
#define API_I_NetPathCanonicalize	137
#define API_I_NetPathCompare		138
#define API_I_NetNameValidate		139
#define API_I_NetNameCanonicalize	140
#define API_I_NetNameCompare		141
#define API_NetAuditRead		142
#define API_WPrintDestAdd		143
#define API_WPrintDestSetInfo		144
#define API_WPrintDestDel		145
#define API_NetUserValidate2		146
#define API_WPrintJobSetInfo		147
#define API_TI_NetServerDiskEnum	148
#define API_TI_NetServerDiskGetInfo	149
#define API_TI_FTVerifyMirror		150
#define API_TI_FTAbortVerify		151
#define API_TI_FTGetInfo		152
#define API_TI_FTSetInfo		153
#define API_TI_FTLockDisk		154
#define API_TI_FTFixError		155
#define API_TI_FTAbortFix		156
#define API_TI_FTDiagnoseError		157
#define API_TI_FTGetDriveStats		158
/* This line and number replaced a Dead Entry for 159 */
#define API_TI_FTErrorGetInfo		160
/* This line and number replaced a Dead Entry for 161 */
/* This line and number replaced a Dead Entry for 162 */
#define API_NetAccessCheck		163
#define API_NetAlertRaise		164
#define API_NetAlertStart		165
#define API_NetAlertStop		166
#define API_NetAuditWrite		167
#define API_NetIRemoteAPI		168
#define API_NetServiceStatus		169
#define API_I_NetServerRegister		170
#define API_I_NetServerDeregister	171
#define API_I_NetSessionEntryMake	172
#define API_I_NetSessionEntryClear	173
#define API_I_NetSessionEntryGetInfo	174
#define API_I_NetSessionEntrySetInfo	175
#define API_I_NetConnectionEntryMake	176
#define API_I_NetConnectionEntryClear	177
#define API_I_NetConnectionEntrySetInfo	178
#define API_I_NetConnectionEntryGetInfo	179
#define API_I_NetFileEntryMake		180
#define API_I_NetFileEntryClear		181
#define API_I_NetFileEntrySetInfo	182
#define API_I_NetFileEntryGetInfo	183
#define API_AltSrvMessageBufferSend	184
#define API_AltSrvMessageFileSend	185
#define API_wI_NetRplWkstaEnum		186
#define API_wI_NetRplWkstaGetInfo	187
#define API_wI_NetRplWkstaSetInfo	188
#define API_wI_NetRplWkstaAdd		189
#define API_wI_NetRplWkstaDel		190
#define API_wI_NetRplProfileEnum	191
#define API_wI_NetRplProfileGetInfo	192
#define API_wI_NetRplProfileSetInfo	193
#define API_wI_NetRplProfileAdd		194
#define API_wI_NetRplProfileDel		195
#define API_wI_NetRplProfileClone	196
#define API_wI_NetRplBaseProfileEnum	197
/* This line and number replaced a Dead Entry for 198 */
/* This line and number replaced a Dead Entry for 199 */
/* This line and number replaced a Dead Entry for 200 */
#define API_WIServerSetInfo		201
/* This line and number replaced a Dead Entry for 202 */
/* This line and number replaced a Dead Entry for 203 */
/* This line and number replaced a Dead Entry for 204 */
#define API_WPrintDriverEnum		205
#define API_WPrintQProcessorEnum	206
#define API_WPrintPortEnum		207
#define API_WNetWriteUpdateLog		208
#define API_WNetAccountUpdate		209
#define API_WNetAccountConfirmUpdate	210
#define API_NetConfigSet		211
#define API_WAccountsReplicate		212
/* 213 is used by WfW */
#define API_SamOEMChgPasswordUser2_P	214
#define API_NetServerEnum3		215
/* XXX - what about 216 through 249? */
#define API_WPrintDriverGetInfo		250
#define API_WPrintDriverSetInfo		251
#define API_NetAliasAdd			252
#define API_NetAliasDel			253
#define API_NetAliasGetInfo		254
#define API_NetAliasSetInfo		255
#define API_NetAliasEnum		256
#define API_NetUserGetLogonAsn		257
#define API_NetUserSetLogonAsn		258
#define API_NetUserGetAppSel		259
#define API_NetUserSetAppSel		260
#define API_NetAppAdd			261
#define API_NetAppDel			262
#define API_NetAppGetInfo		263
#define API_NetAppSetInfo		264
#define API_NetAppEnum			265
#define API_NetUserDCDBInit		266
#define API_NetDASDAdd			267
#define API_NetDASDDel			268
#define API_NetDASDGetInfo		269
#define API_NetDASDSetInfo		270
#define API_NetDASDEnum			271
#define API_NetDASDCheck		272
#define API_NetDASDCtl			273
#define API_NetUserRemoteLogonCheck	274
#define API_NetUserPasswordSet3		275
#define API_NetCreateRIPLMachine	276
#define API_NetDeleteRIPLMachine	277
#define API_NetGetRIPLMachineInfo	278
#define API_NetSetRIPLMachineInfo	279
#define API_NetEnumRIPLMachine		280
#define API_I_ShareAdd			281
#define API_I_AliasEnum			282
#define API_NetAccessApply		283
#define API_WPrt16Query			284
#define API_WPrt16Set			285
#define API_NetUserDel100		286
#define API_NetUserRemoteLogonCheck2	287
#define API_WRemoteTODSet		294
#define API_WPrintJobMoveAll		295
#define API_W16AppParmAdd		296
#define API_W16AppParmDel		297
#define API_W16AppParmGet		298
#define API_W16AppParmSet		299
#define API_W16RIPLMachineCreate	300
#define API_W16RIPLMachineGetInfo	301
#define API_W16RIPLMachineSetInfo	302
#define API_W16RIPLMachineEnum		303
#define API_W16RIPLMachineListParmEnum	304
#define API_W16RIPLMachClassGetInfo	305
#define API_W16RIPLMachClassEnum	306
#define API_W16RIPLMachClassCreate	307
#define API_W16RIPLMachClassSetInfo	308
#define API_W16RIPLMachClassDelete	309
#define API_W16RIPLMachClassLPEnum	310
#define API_W16RIPLMachineDelete	311
#define API_W16WSLevelGetInfo		312
#define API_NetServerNameAdd		313
#define API_NetServerNameDel		314
#define API_NetServerNameEnum		315
#define API_I_WDASDEnum			316
#define API_I_WDASDEnumTerminate	317
#define API_I_WDASDSetInfo2		318

static const struct lanman_desc lmd[] = {
	{ API_NetShareEnum,
	  lm_params_req_netshareenum,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netshareenum,
	  "Available Shares",
	  &ett_lanman_shares,
	  netshareenum_share_entry,
	  &ett_lanman_share,
	  lm_data_resp_netshareenum,
	  lm_null },

	{ API_NetShareGetInfo,
	  lm_params_req_netsharegetinfo,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netsharegetinfo,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netsharegetinfo,
	  lm_null },

	{ API_NetServerGetInfo, 
	  lm_params_req_netservergetinfo,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netservergetinfo,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_serverinfo,
	  lm_null },

	{ API_NetUserGetInfo,
	  lm_params_req_netusergetinfo,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netusergetinfo,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netusergetinfo,
	  lm_null },

	{ API_NetUserGetGroups,
	  lm_params_req_netusergetgroups,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netusergetgroups,
	  "Groups",
	  &ett_lanman_groups,
	  NULL,
	  NULL,
	  lm_data_resp_netusergetgroups,
	  lm_null },

	{ API_NetRemoteTOD,
	  lm_null,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_null,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netremotetod,
	  lm_null },

	{ API_NetServerEnum2,
	  lm_params_req_netserverenum2,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netserverenum2,
	  "Servers",
	  &ett_lanman_servers,
	  netserverenum2_server_entry,
	  &ett_lanman_server,
	  lm_data_serverinfo,
	  lm_null },

	{ API_NetWkstaGetInfo,
	  lm_params_req_netwkstagetinfo,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netwkstagetinfo,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netwkstagetinfo,
	  lm_null },

	{ API_NetWkstaUserLogon,
	  lm_params_req_netwkstauserlogon,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netwkstauserlogon,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netwkstauserlogon,
	  lm_null },

	{ API_NetWkstaUserLogoff,
	  lm_params_req_netwkstauserlogoff,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_params_resp_netwkstauserlogoff,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_data_resp_netwkstauserlogoff,
	  lm_null },

	{ API_SamOEMChgPasswordUser2_P,
	  lm_params_req_samoemchangepassword,
	  NULL,
	  NULL,
	  lm_data_req_samoemchangepassword,
	  lm_null,
	  lm_null,
	  NULL,
	  NULL,
	  NULL,
	  NULL,
	  lm_null_list,
	  lm_null },

	{ -1,
	  lm_null,
	  NULL,
	  NULL,
	  lm_null,
	  lm_null,
	  lm_null,
	  NULL,
	  NULL,
	  NULL,
	  &ett_lanman_unknown_entry,
	  lm_null_list,
	  lm_null }
};

static const struct lanman_desc *
find_lanman(int lanman_num)
{
	int i;

	for (i = 0; lmd[i].lanman_num != -1; i++) {
		if (lmd[i].lanman_num == lanman_num)
			break;
	}
	return &lmd[i];
}

static const guchar *
get_count(const guchar *desc, int *countp)
{
	int count = 0;
	guchar c;

	if (!isdigit(*desc)) {
		*countp = 1;	/* no count was supplied */
		return desc;
	}

	while ((c = *desc) != '\0' && isdigit(c)) {
		count = (count * 10) + c - '0';
		desc++;
	}

	*countp = count;	/* XXX - what if it's 0? */
	return desc;
}

static int
dissect_request_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
    proto_tree *tree, const guchar *desc, const item_t *items,
    gboolean *has_data_p)
{
	guint c;
	guint16 WParam;
	guint32 LParam;
	guint string_len;
	int count;

	*has_data_p = FALSE;
	while ((c = *desc++) != '\0') {
		switch (c) {

		case 'W':
			/*
			 * A 16-bit word value in the request.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_word_param(tvb, offset, 0, pinfo,
				    tree, 0, -1);
			} else if (items->type != PARAM_WORD) {
				/*
				 * Descriptor character is 'W', but this
				 * isn't a word parameter.
				 */
				WParam = tvb_get_letohs(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%04X), type is wrong (W)",
				    (*items->hf_index == -1) ?
				      "Word Param" :
				      proto_registrar_get_name(*items->hf_index),
				    WParam, WParam);
				offset += 2;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'D':
			/*
			 * A 32-bit doubleword value in the request.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_dword_param(tvb, offset, 0, pinfo,
				    tree, 0, -1);
			} else if (items->type != PARAM_DWORD) {
				/*
				 * Descriptor character is 'D', but this
				 * isn't a doubleword parameter.
				 */
				LParam = tvb_get_letohl(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%08X), type is wrong (D)",
				    (*items->hf_index == -1) ?
				      "Doubleword Param" :
				      proto_registrar_get_name(*items->hf_index),
				    LParam, LParam);
				offset += 4;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'b':
			/*
			 * A byte or multi-byte value in the request.
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_byte_param(tvb, offset, count,
				    pinfo, tree, 0, -1);
			} else if (items->type != PARAM_BYTES) {
				/*
				 * Descriptor character is 'b', but this
				 * isn't a byte/bytes parameter.
				 */
				proto_tree_add_text(tree, tvb, offset, count,
				    "%s: Value is %s, type is wrong (b)",
				    (*items->hf_index == -1) ?
				      "Byte Param" :
				      proto_registrar_get_name(*items->hf_index),
				    tvb_bytes_to_str(tvb, offset, count));
				offset += count;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, count,
				    pinfo, tree, 0, *items->hf_index);
				items++;
			}
 			break;

		case 'O':
			/*
			 * A null pointer.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				add_null_pointer_param(tvb, offset, 0,
				    pinfo, tree, 0, -1);
			} else {
				/*
				 * If "*items->hf_index" is -1, this is
				 * a reserved must-be-null field; don't
				 * clutter the protocol tree by putting
				 * it in.
				 */
				if (*items->hf_index != -1) {
					add_null_pointer_param(tvb,
					    offset, 0, pinfo, tree, 0,
					    *items->hf_index);
				}
				items++;
			}
			break;

		case 'z':
			/*
			 * A null-terminated ASCII string.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_string_param(tvb, offset, 0,
				    pinfo, tree, 0, -1);
			} else if (items->type != PARAM_STRINGZ) {
				/*
				 * Descriptor character is 'z', but this
				 * isn't a string parameter.
				 */
				string_len = tvb_strsize(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, string_len,
				    "%s: Value is %s, type is wrong (z)",
				    (*items->hf_index == -1) ?
				      "String Param" :
				      proto_registrar_get_name(*items->hf_index),
				    tvb_format_text(tvb, offset, string_len));
				offset += string_len;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0,
				    pinfo, tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'F':
			/*
			 * One or more pad bytes.
			 */
			desc = get_count(desc, &count);
			proto_tree_add_text(tree, tvb, offset, count,
			    "%s", "Padding");
			offset += count;
			break;

		case 'L':
			/*
			 * 16-bit receive buffer length.
			 */
			proto_tree_add_item(tree, hf_recv_buf_len, tvb,
			    offset, 2, TRUE);
			offset += 2;
			break;

		case 's':
			/*
			 * 32-bit send buffer offset.
			 * This appears not to be sent over the wire.
			 */
			*has_data_p = TRUE;
			break;

		case 'T':
			/*
			 * 16-bit send buffer length.
			 * This also appears not to be sent over the wire.
			 */
			break;

		default:
			break;
		}
	}
	return offset;
}

static int
dissect_response_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
    proto_tree *tree, const guchar *desc, const item_t *items,
    gboolean *has_data_p, gboolean *has_ent_count_p, guint16 *ent_count_p)
{
	guint c;
	guint16 WParam;
	guint32 LParam;
	int count;

	*has_data_p = FALSE;
	*has_ent_count_p = FALSE;
	while ((c = *desc++) != '\0') {
		switch (c) {

		case 'r':
			/*
			 * 32-bit receive buffer offset.
			 */
			*has_data_p = TRUE;
			break;

		case 'g':
			/*
			 * A byte or series of bytes is returned.
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_byte_param(tvb, offset, count,
				    pinfo, tree, 0, -1);
			} else if (items->type != PARAM_BYTES) {
				/*
				 * Descriptor character is 'b', but this
				 * isn't a byte/bytes parameter.
				 */
				proto_tree_add_text(tree, tvb, offset, count,
				    "%s: Value is %s, type is wrong (g)",
				    (*items->hf_index == -1) ?
				      "Byte Param" :
				      proto_registrar_get_name(*items->hf_index),
				    tvb_bytes_to_str(tvb, offset, count));
				offset += count;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, count,
				    pinfo, tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'h':
			/*
			 * A 16-bit word is received.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_word_param(tvb, offset, 0, pinfo,
				    tree, 0, -1);
			} else if (items->type != PARAM_WORD) {
				/*
				 * Descriptor character is 'h', but this
				 * isn't a word parameter.
				 */
				WParam = tvb_get_letohs(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%04X), type is wrong (W)",
				    (*items->hf_index == -1) ?
				      "Word Param" :
				      proto_registrar_get_name(*items->hf_index),
				    WParam, WParam);
				offset += 2;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'i':
			/*
			 * A 32-bit doubleword is received.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_dword_param(tvb, offset, 0, pinfo,
				    tree, 0, -1);
			} else if (items->type != PARAM_DWORD) {
				/*
				 * Descriptor character is 'i', but this
				 * isn't a doubleword parameter.
				 */
				LParam = tvb_get_letohl(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%08X), type is wrong (i)",
				    (*items->hf_index == -1) ?
				      "Doubleword Param" :
				      proto_registrar_get_name(*items->hf_index),
				    LParam, LParam);
				offset += 4;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, 0, *items->hf_index);
				items++;
			}
			break;

		case 'e':
			/*
			 * A 16-bit entry count is returned.
			 */
			WParam = tvb_get_letohs(tvb, offset);
			proto_tree_add_uint(tree, hf_ecount, tvb, offset, 2,
			    WParam);
			offset += 2;
			*has_ent_count_p = TRUE;
			*ent_count_p = WParam;  /* Save this for later retrieval */
			break;

		default:
			break;
		}
	}
	return offset;
}

static int
dissect_transact_data(tvbuff_t *tvb, int offset, int convert,
    packet_info *pinfo, proto_tree *tree, const guchar *desc,
    const item_t *items, guint16 *aux_count_p)
{
	guint c;
	guint16 WParam;
	guint32 LParam;
	int count;
	int cptr;
	const char *string;
	gint string_len;
	const guint8 *bytes;

	if (aux_count_p != NULL)
		*aux_count_p = 0;

	while ((c = *desc++) != '\0') {
		switch (c) {

		case 'W':
			/*
			 * A 16-bit word value.
			 * XXX - handle the count?
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_word_param(tvb, offset, 0, pinfo,
				    tree, convert, -1);
			} else if (items->type != PARAM_WORD) {
				/*
				 * Descriptor character is 'W', but this
				 * isn't a word parameter.
				 */
				WParam = tvb_get_letohs(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%04X), type is wrong (W)",
				    (*items->hf_index == -1) ?
				      "Word Param" :
				      proto_registrar_get_name(*items->hf_index),
				    WParam, WParam);
				offset += 2;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, convert, *items->hf_index);
				items++;
			}
			break;

		case 'D':
			/*
			 * A 32-bit doubleword value.
			 * XXX - handle the count?
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_dword_param(tvb, offset, 0, pinfo,
				    tree, convert, -1);
			} else if (items->type != PARAM_DWORD) {
				/*
				 * Descriptor character is 'D', but this
				 * isn't a doubleword parameter.
				 */
				LParam = tvb_get_letohl(tvb, offset);
				proto_tree_add_text(tree, tvb, offset, 2,
				    "%s: Value is %u (0x%08X), type is wrong (D)",
				    (*items->hf_index == -1) ?
				      "Doubleword Param" :
				      proto_registrar_get_name(*items->hf_index),
				    LParam, LParam);
				offset += 4;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0, pinfo,
				    tree, convert, *items->hf_index);
				items++;
			}
			break;

		case 'B':
			/*
			 * A byte or multi-byte value.
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_byte_param(tvb, offset, count,
				    pinfo, tree, convert, -1);
			} else if (items->type != PARAM_BYTES) {
				/*
				 * Descriptor character is 'B', but this
				 * isn't a byte/bytes parameter.
				 */
				proto_tree_add_text(tree, tvb, offset, count,
				    "%s: Value is %s, type is wrong (B)",
				    (*items->hf_index == -1) ?
				      "Byte Param" :
				      proto_registrar_get_name(*items->hf_index),
				    tvb_bytes_to_str(tvb, offset, count));
				offset += count;
				items++;
			} else {
				offset = (*items->func)(tvb, offset, count,
				    pinfo, tree, convert, *items->hf_index);
				items++;
			}
			break;

		case 'O':
			/*
			 * A null pointer.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				add_null_pointer_param(tvb, offset, 0,
				    pinfo, tree, convert, -1);
			} else {
				/*
				 * If "*items->hf_index" is -1, this is
				 * a reserved must-be-null field; don't
				 * clutter the protocol tree by putting
				 * it in.
				 */
				if (*items->hf_index != -1) {
					add_null_pointer_param(tvb,
					    offset, 0, pinfo, tree, convert,
					    *items->hf_index);
				}
				items++;
			}
			break;

		case 'z':
			/*
			 * A pointer to a null-terminated ASCII string.
			 */
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_stringz_pointer_param(tvb, offset,
				    0, pinfo, tree, convert, -1);
			} else if (items->type != PARAM_STRINGZ) {
				/*
				 * Descriptor character is 'z', but this
				 * isn't a string parameter.
				 */
				string = get_stringz_pointer_value(tvb, offset,
				    convert, &cptr, &string_len);
				offset += 4;
				proto_tree_add_text(tree, tvb, cptr, string_len,
				    "%s: Value is %s, type is wrong (z)",
				    (*items->hf_index == -1) ?
				      "String Param" :
				      proto_registrar_get_name(*items->hf_index),
				    string);
				items++;
			} else {
				offset = (*items->func)(tvb, offset, 0,
				    pinfo, tree, convert, *items->hf_index);
				items++;
			}
			break;

		case 'b':
			/*
			 * A pointer to a byte or multi-byte value.
			 */
			desc = get_count(desc, &count);
			if (items->func == NULL) {
				/*
				 * We've run out of items in the table;
				 * fall back on the default.
				 */
				offset = add_bytes_pointer_param(tvb, offset,
				    count, pinfo, tree, convert, -1);
			} else if (items->type != PARAM_BYTES) {
				/*
				 * Descriptor character is 'b', but this
				 * isn't a byte/bytes parameter.
				 */
				cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
				offset += 4;
				proto_tree_add_text(tree, tvb, offset, count,
				    "%s: Value is %s, type is wrong (b)",
				    (*items->hf_index == -1) ?
				      "Byte Param" :
				      proto_registrar_get_name(*items->hf_index),
				    tvb_bytes_to_str(tvb, cptr, count));
				items++;
			} else {
				offset = (*items->func)(tvb, offset, count,
				    pinfo, tree, convert, *items->hf_index);
				items++;
			}
			break;

		case 'N':
			/*
			 * 16-bit auxiliary data structure count.
			 * XXX - hf_acount?
			 */
			WParam = tvb_get_letohs(tvb, offset);
			proto_tree_add_text(tree, tvb, offset, 2,
			    "%s: %u (0x%04X)",
			    "Auxiliary data structure count",
			    WParam, WParam);
			offset += 2;
			if (aux_count_p != NULL)
				*aux_count_p = WParam;  /* Save this for later retrieval */
			break;

		default:
			break;
		}
	}
	return offset;
}

static const value_string commands[] = {
	{API_NetShareEnum,			"NetShareEnum"},
	{API_NetShareGetInfo,			"NetShareGetInfo"},
	{API_NetShareSetInfo,			"NetShareSetInfo"},
	{API_NetShareAdd,			"NetShareAdd"},
	{API_NetShareDel,			"NetShareDel"},
	{API_NetShareCheck,			"NetShareCheck"},
	{API_NetSessionEnum,			"NetSessionEnum"},
	{API_NetSessionGetInfo,			"NetSessionGetInfo"},
	{API_NetSessionDel,			"NetSessionDel"},
	{API_WconnectionEnum,			"NetConnectionEnum"},
	{API_NetFileEnum,			"NetFileEnum"},
	{API_NetFileGetInfo,			"NetFileGetInfo"},
	{API_NetFileClose,			"NetFileClose"},
	{API_NetServerGetInfo,			"NetServerGetInfo"},
	{API_NetServerSetInfo,			"NetServerSetInfo"},
	{API_NetServerDiskEnum,			"NetServerDiskEnum"},
	{API_NetServerAdminCommand,		"NetServerAdminCommand"},
	{API_NetAuditOpen,			"NetAuditOpen"},
	{API_NetAuditClear,			"NetAuditClear"},
	{API_NetErrorLogOpen,			"NetErrorLogOpen"},
	{API_NetErrorLogClear,			"NetErrorLogClear"},
	{API_NetCharDevEnum,			"NetCharDevEnum"},
	{API_NetCharDevGetInfo,			"NetCharDevGetInfo"},
	{API_NetCharDevControl,			"NetCharDevControl"},
	{API_NetCharDevQEnum,			"NetCharDevQEnum"},
	{API_NetCharDevQGetInfo,		"NetCharDevQGetInfo"},
	{API_NetCharDevQSetInfo,		"NetCharDevQSetInfo"},
	{API_NetCharDevQPurge,			"NetCharDevQPurge"},
	{API_NetCharDevQPurgeSelf,		"NetCharDevQPurgeSelf"},
	{API_NetMessageNameEnum,		"NetMessageNameEnum"},
	{API_NetMessageNameGetInfo,		"NetMessageNameGetInfo"},
	{API_NetMessageNameAdd,			"NetMessageNameAdd"},
	{API_NetMessageNameDel,			"NetMessageNameDel"},
	{API_NetMessageNameFwd,			"NetMessageNameFwd"},
	{API_NetMessageNameUnFwd,		"NetMessageNameUnFwd"},
	{API_NetMessageBufferSend,		"NetMessageBufferSend"},
	{API_NetMessageFileSend,		"NetMessageFileSend"},
	{API_NetMessageLogFileSet,		"NetMessageLogFileSet"},
	{API_NetMessageLogFileGet,		"NetMessageLogFileGet"},
	{API_NetServiceEnum,			"NetServiceEnum"},
	{API_NetServiceInstall,			"NetServiceInstall"},
	{API_NetServiceControl,			"NetServiceControl"},
	{API_NetAccessEnum,			"NetAccessEnum"},
	{API_NetAccessGetInfo,			"NetAccessGetInfo"},
	{API_NetAccessSetInfo,			"NetAccessSetInfo"},
	{API_NetAccessAdd,			"NetAccessAdd"},
	{API_NetAccessDel,			"NetAccessDel"},
	{API_NetGroupEnum,			"NetGroupEnum"},
	{API_NetGroupAdd,			"NetGroupAdd"},
	{API_NetGroupDel,			"NetGroupDel"},
	{API_NetGroupAddUser,			"NetGroupAddUser"},
	{API_NetGroupDelUser,			"NetGroupDelUser"},
	{API_NetGroupGetUsers,			"NetGroupGetUsers"},
	{API_NetUserEnum,			"NetUserEnum"},
	{API_NetUserAdd,			"NetUserAdd"},
	{API_NetUserDel,			"NetUserDel"},
	{API_NetUserGetInfo,			"NetUserGetInfo"},
	{API_NetUserSetInfo,			"NetUserSetInfo"},
	{API_NetUserPasswordSet,		"NetUserPasswordSet"},
	{API_NetUserGetGroups,			"NetUserGetGroups"},
	{API_NetWkstaSetUID,			"NetWkstaSetUID"},
	{API_NetWkstaGetInfo,			"NetWkstaGetInfo"},
	{API_NetWkstaSetInfo,			"NetWkstaSetInfo"},
	{API_NetUseEnum,			"NetUseEnum"},
	{API_NetUseAdd,				"NetUseAdd"},
	{API_NetUseDel,				"NetUseDel"},
	{API_NetUseGetInfo,			"NetUseGetInfo"},
	{API_WPrintQEnum,			"WPrintQEnum"},
	{API_WPrintQGetInfo,			"WPrintQGetInfo"},
	{API_WPrintQSetInfo,			"WPrintQSetInfo"},
	{API_WPrintQAdd,			"WPrintQAdd"},
	{API_WPrintQDel,			"WPrintQDel"},
	{API_WPrintQPause,			"WPrintQPause"},
	{API_WPrintQContinue,			"WPrintQContinue"},
	{API_WPrintJobEnum,			"WPrintJobEnum"},
	{API_WPrintJobGetInfo,			"WPrintJobGetInfo"},
	{API_WPrintJobSetInfo_OLD,		"WPrintJobSetInfo_OLD"},
	{API_WPrintJobDel,			"WPrintJobDel"},
	{API_WPrintJobPause,			"WPrintJobPause"},
	{API_WPrintJobContinue,			"WPrintJobContinue"},
	{API_WPrintDestEnum,			"WPrintDestEnum"},
	{API_WPrintDestGetInfo,			"WPrintDestGetInfo"},
	{API_WPrintDestControl,			"WPrintDestControl"},
	{API_NetProfileSave,			"NetProfileSave"},
	{API_NetProfileLoad,			"NetProfileLoad"},
	{API_NetStatisticsGet,			"NetStatisticsGet"},
	{API_NetStatisticsClear,		"NetStatisticsClear"},
	{API_NetRemoteTOD,			"NetRemoteTOD"},
	{API_WNetBiosEnum,			"WNetBiosEnum"},
	{API_WNetBiosGetInfo,			"WNetBiosGetInfo"},
	{API_NetServerEnum,			"NetServerEnum"},
	{API_I_NetServerEnum,			"I_NetServerEnum"},
	{API_NetServiceGetInfo,			"NetServiceGetInfo"},
	{API_WPrintQPurge,			"WPrintQPurge"},
	{API_NetServerEnum2,			"NetServerEnum2"},
	{API_NetAccessGetUserPerms,		"NetAccessGetUserPerms"},
	{API_NetGroupGetInfo,			"NetGroupGetInfo"},
	{API_NetGroupSetInfo,			"NetGroupSetInfo"},
	{API_NetGroupSetUsers,			"NetGroupSetUsers"},
	{API_NetUserSetGroups,			"NetUserSetGroups"},
	{API_NetUserModalsGet,			"NetUserModalsGet"},
	{API_NetUserModalsSet,			"NetUserModalsSet"},
	{API_NetFileEnum2,			"NetFileEnum2"},
	{API_NetUserAdd2,			"NetUserAdd2"},
	{API_NetUserSetInfo2,			"NetUserSetInfo2"},
	{API_NetUserPasswordSet2,		"SetUserPassword"},
	{API_I_NetServerEnum2,			"I_NetServerEnum2"},
	{API_NetConfigGet2,			"NetConfigGet2"},
	{API_NetConfigGetAll2,			"NetConfigGetAll2"},
	{API_NetGetDCName,			"NetGetDCName"},
	{API_NetHandleGetInfo,			"NetHandleGetInfo"},
	{API_NetHandleSetInfo,			"NetHandleSetInfo"},
	{API_NetStatisticsGet2,			"NetStatisticsGet2"},
	{API_WBuildGetInfo,			"WBuildGetInfo"},
	{API_NetFileGetInfo2,			"NetFileGetInfo2"},
	{API_NetFileClose2,			"NetFileClose2"},
	{API_NetServerReqChallenge,		"NetServerReqChallenge"},
	{API_NetServerAuthenticate,		"NetServerAuthenticate"},
	{API_NetServerPasswordSet,		"NetServerPasswordSet"},
	{API_WNetAccountDeltas,			"WNetAccountDeltas"},
	{API_WNetAccountSync,			"WNetAccountSync"},
	{API_NetUserEnum2,			"NetUserEnum2"},
	{API_NetWkstaUserLogon,			"NetWkstaUserLogon"},
	{API_NetWkstaUserLogoff,		"NetWkstaUserLogoff"},
	{API_NetLogonEnum,			"NetLogonEnum"},
	{API_NetErrorLogRead,			"NetErrorLogRead"},
	{API_I_NetPathType,			"I_NetPathType"},
	{API_I_NetPathCanonicalize,		"I_NetPathCanonicalize"},
	{API_I_NetPathCompare,			"I_NetPathCompare"},
	{API_I_NetNameValidate,			"I_NetNameValidate"},
	{API_I_NetNameCanonicalize,		"I_NetNameCanonicalize"},
	{API_I_NetNameCompare,			"I_NetNameCompare"},
	{API_NetAuditRead,			"NetAuditRead"},
	{API_WPrintDestAdd,			"WPrintDestAdd"},
	{API_WPrintDestSetInfo,			"WPrintDestSetInfo"},
	{API_WPrintDestDel,			"WPrintDestDel"},
	{API_NetUserValidate2,			"NetUserValidate2"},
	{API_WPrintJobSetInfo,			"WPrintJobSetInfo"},
	{API_TI_NetServerDiskEnum,		"TI_NetServerDiskEnum"},
	{API_TI_NetServerDiskGetInfo,		"TI_NetServerDiskGetInfo"},
	{API_TI_FTVerifyMirror,			"TI_FTVerifyMirror"},
	{API_TI_FTAbortVerify,			"TI_FTAbortVerify"},
	{API_TI_FTGetInfo,			"TI_FTGetInfo"},
	{API_TI_FTSetInfo,			"TI_FTSetInfo"},
	{API_TI_FTLockDisk,			"TI_FTLockDisk"},
	{API_TI_FTFixError,			"TI_FTFixError"},
	{API_TI_FTAbortFix,			"TI_FTAbortFix"},
	{API_TI_FTDiagnoseError,		"TI_FTDiagnoseError"},
	{API_TI_FTGetDriveStats,		"TI_FTGetDriveStats"},
	{API_TI_FTErrorGetInfo,			"TI_FTErrorGetInfo"},
	{API_NetAccessCheck,			"NetAccessCheck"},
	{API_NetAlertRaise,			"NetAlertRaise"},
	{API_NetAlertStart,			"NetAlertStart"},
	{API_NetAlertStop,			"NetAlertStop"},
	{API_NetAuditWrite,			"NetAuditWrite"},
	{API_NetIRemoteAPI,			"NetIRemoteAPI"},
	{API_NetServiceStatus,			"NetServiceStatus"},
	{API_I_NetServerRegister,		"I_NetServerRegister"},
	{API_I_NetServerDeregister,		"I_NetServerDeregister"},
	{API_I_NetSessionEntryMake,		"I_NetSessionEntryMake"},
	{API_I_NetSessionEntryClear,		"I_NetSessionEntryClear"},
	{API_I_NetSessionEntryGetInfo,		"I_NetSessionEntryGetInfo"},
	{API_I_NetSessionEntrySetInfo,		"I_NetSessionEntrySetInfo"},
	{API_I_NetConnectionEntryMake,		"I_NetConnectionEntryMake"},
	{API_I_NetConnectionEntryClear,		"I_NetConnectionEntryClear"},
	{API_I_NetConnectionEntrySetInfo,	"I_NetConnectionEntrySetInfo"},
	{API_I_NetConnectionEntryGetInfo,	"I_NetConnectionEntryGetInfo"},
	{API_I_NetFileEntryMake,		"I_NetFileEntryMake"},
	{API_I_NetFileEntryClear,		"I_NetFileEntryClear"},
	{API_I_NetFileEntrySetInfo,		"I_NetFileEntrySetInfo"},
	{API_I_NetFileEntryGetInfo,		"I_NetFileEntryGetInfo"},
	{API_AltSrvMessageBufferSend,		"AltSrvMessageBufferSend"},
	{API_AltSrvMessageFileSend,		"AltSrvMessageFileSend"},
	{API_wI_NetRplWkstaEnum,		"wI_NetRplWkstaEnum"},
	{API_wI_NetRplWkstaGetInfo,		"wI_NetRplWkstaGetInfo"},
	{API_wI_NetRplWkstaSetInfo,		"wI_NetRplWkstaSetInfo"},
	{API_wI_NetRplWkstaAdd,			"wI_NetRplWkstaAdd"},
	{API_wI_NetRplWkstaDel,			"wI_NetRplWkstaDel"},
	{API_wI_NetRplProfileEnum,		"wI_NetRplProfileEnum"},
	{API_wI_NetRplProfileGetInfo,		"wI_NetRplProfileGetInfo"},
	{API_wI_NetRplProfileSetInfo,		"wI_NetRplProfileSetInfo"},
	{API_wI_NetRplProfileAdd,		"wI_NetRplProfileAdd"},
	{API_wI_NetRplProfileDel,		"wI_NetRplProfileDel"},
	{API_wI_NetRplProfileClone,		"wI_NetRplProfileClone"},
	{API_wI_NetRplBaseProfileEnum,		"wI_NetRplBaseProfileEnum"},
	{API_WIServerSetInfo,			"WIServerSetInfo"},
	{API_WPrintDriverEnum,			"WPrintDriverEnum"},
	{API_WPrintQProcessorEnum,		"WPrintQProcessorEnum"},
	{API_WPrintPortEnum,			"WPrintPortEnum"},
	{API_WNetWriteUpdateLog,		"WNetWriteUpdateLog"},
	{API_WNetAccountUpdate,			"WNetAccountUpdate"},
	{API_WNetAccountConfirmUpdate,		"WNetAccountConfirmUpdate"},
	{API_NetConfigSet,			"NetConfigSet"},
	{API_WAccountsReplicate,		"WAccountsReplicate"},
	{API_SamOEMChgPasswordUser2_P,		"SamOEMChangePassword"},
	{API_NetServerEnum3,			"NetServerEnum3"},
	{API_WPrintDriverGetInfo,		"WPrintDriverGetInfo"},
	{API_WPrintDriverSetInfo,		"WPrintDriverSetInfo"},
	{API_NetAliasAdd,			"NetAliasAdd"},
	{API_NetAliasDel,			"NetAliasDel"},
	{API_NetAliasGetInfo,			"NetAliasGetInfo"},
	{API_NetAliasSetInfo,			"NetAliasSetInfo"},
	{API_NetAliasEnum,			"NetAliasEnum"},
	{API_NetUserGetLogonAsn,		"NetUserGetLogonAsn"},
	{API_NetUserSetLogonAsn,		"NetUserSetLogonAsn"},
	{API_NetUserGetAppSel,			"NetUserGetAppSel"},
	{API_NetUserSetAppSel,			"NetUserSetAppSel"},
	{API_NetAppAdd,				"NetAppAdd"},
	{API_NetAppDel,				"NetAppDel"},
	{API_NetAppGetInfo,			"NetAppGetInfo"},
	{API_NetAppSetInfo,			"NetAppSetInfo"},
	{API_NetAppEnum,			"NetAppEnum"},
	{API_NetUserDCDBInit,			"NetUserDCDBInit"},
	{API_NetDASDAdd,			"NetDASDAdd"},
	{API_NetDASDDel,			"NetDASDDel"},
	{API_NetDASDGetInfo,			"NetDASDGetInfo"},
	{API_NetDASDSetInfo,			"NetDASDSetInfo"},
	{API_NetDASDEnum,			"NetDASDEnum"},
	{API_NetDASDCheck,			"NetDASDCheck"},
	{API_NetDASDCtl,			"NetDASDCtl"},
	{API_NetUserRemoteLogonCheck,		"NetUserRemoteLogonCheck"},
	{API_NetUserPasswordSet3,		"NetUserPasswordSet3"},
	{API_NetCreateRIPLMachine,		"NetCreateRIPLMachine"},
	{API_NetDeleteRIPLMachine,		"NetDeleteRIPLMachine"},
	{API_NetGetRIPLMachineInfo,		"NetGetRIPLMachineInfo"},
	{API_NetSetRIPLMachineInfo,		"NetSetRIPLMachineInfo"},
	{API_NetEnumRIPLMachine,		"NetEnumRIPLMachine"},
	{API_I_ShareAdd,			"I_ShareAdd"},
	{API_I_AliasEnum,			"I_AliasEnum"},
	{API_NetAccessApply,			"NetAccessApply"},
	{API_WPrt16Query,			"WPrt16Query"},
	{API_WPrt16Set,				"WPrt16Set"},
	{API_NetUserDel100,			"NetUserDel100"},
	{API_NetUserRemoteLogonCheck2,		"NetUserRemoteLogonCheck2"},
	{API_WRemoteTODSet,			"WRemoteTODSet"},
	{API_WPrintJobMoveAll,			"WPrintJobMoveAll"},
	{API_W16AppParmAdd,			"W16AppParmAdd"},
	{API_W16AppParmDel,			"W16AppParmDel"},
	{API_W16AppParmGet,			"W16AppParmGet"},
	{API_W16AppParmSet,			"W16AppParmSet"},
	{API_W16RIPLMachineCreate,		"W16RIPLMachineCreate"},
	{API_W16RIPLMachineGetInfo,		"W16RIPLMachineGetInfo"},
	{API_W16RIPLMachineSetInfo,		"W16RIPLMachineSetInfo"},
	{API_W16RIPLMachineEnum,		"W16RIPLMachineEnum"},
	{API_W16RIPLMachineListParmEnum,	"W16RIPLMachineListParmEnum"},
	{API_W16RIPLMachClassGetInfo,		"W16RIPLMachClassGetInfo"},
	{API_W16RIPLMachClassEnum,		"W16RIPLMachClassEnum"},
	{API_W16RIPLMachClassCreate,		"W16RIPLMachClassCreate"},
	{API_W16RIPLMachClassSetInfo,		"W16RIPLMachClassSetInfo"},
	{API_W16RIPLMachClassDelete,		"W16RIPLMachClassDelete"},
	{API_W16RIPLMachClassLPEnum,		"W16RIPLMachClassLPEnum"},
	{API_W16RIPLMachineDelete,		"W16RIPLMachineDelete"},
	{API_W16WSLevelGetInfo,			"W16WSLevelGetInfo"},
	{API_NetServerNameAdd,			"NetServerNameAdd"},
	{API_NetServerNameDel,			"NetServerNameDel"},
	{API_NetServerNameEnum,			"NetServerNameEnum"},
	{API_I_WDASDEnum,			"I_WDASDEnum"},
	{API_I_WDASDEnumTerminate,		"I_WDASDEnumTerminate"},
	{API_I_WDASDSetInfo2,			"I_WDASDSetInfo2"},
	{0,					NULL}
};

static void
dissect_response_data(tvbuff_t *tvb, packet_info *pinfo, int convert,
    proto_tree *tree, struct smb_info *smb_info,
    const struct lanman_desc *lanman, gboolean has_ent_count,
    guint16 ent_count)
{
	smb_transact_info_t *trp = smb_info->sip->extra_info;
	const item_list_t *resp_data_list;
	int offset, start_offset;
	const char *label;
	gint ett;
	const item_t *resp_data;
	proto_item *data_item;
	proto_tree *data_tree;
	proto_item *entry_item;
	proto_tree *entry_tree;
	guint i, j;
	guint16 aux_count;

	/*
	 * Find the item table for the matching request's detail level.
	 */
	for (resp_data_list = lanman->resp_data_list;
	    resp_data_list->level != -1; resp_data_list++) {
		if (resp_data_list->level == trp->info_level)
			break;
	}
	resp_data = resp_data_list->item_list;

	offset = 0;
	if (has_ent_count) {
		/*
		 * The data is a list of entries; create a protocol tree item
		 * for it.
		 */
		if (tree) {
			label = lanman->resp_data_entry_list_label;
			if (label == NULL)
				label = "Entries";
			if (lanman->ett_data_entry_list != NULL)
				ett = *lanman->ett_data_entry_list;
			else
				ett = ett_lanman_unknown_entries;
			data_item = proto_tree_add_text(tree, tvb, offset, -1,
			    label);
			data_tree = proto_item_add_subtree(data_item, ett);
		} else {
			data_item = NULL;
			data_tree = NULL;
		}
	} else {
		/*
		 * Just leave it at the top level.
		 */
		data_item = NULL;
		data_tree = tree;
	}

	if (trp->data_descrip == NULL) {
		/*
		 * This could happen if we only dissected
		 * part of the request to which this is a
		 * reply, e.g. if the request was split
		 * across TCP segments and we weren't doing
		 * TCP desegmentation, or if we had a snapshot
		 * length that was too short.
		 *
		 * We can't dissect the data; just show it
		 * as raw data.
		 */
		proto_tree_add_text(tree, tvb, offset, -1,
		    "Data (no descriptor available)");
		offset += tvb_length_remaining(tvb, offset);
	} else {
		/*
		 * If we have an entry count, show all the entries,
		 * with each one having a protocol tree item.
		 *
		 * Otherwise, we just show one returned item, with
		 * no protocol tree item.
		 */
		if (!has_ent_count)
			ent_count = 1;
		for (i = 0; i < ent_count; i++) {
			start_offset = offset;
			if (has_ent_count &&
			    lanman->resp_data_element_item != NULL) {
				/*
				 * Create a protocol tree item for the
				 * entry.
				 */
				entry_item =
				    (*lanman->resp_data_element_item)
				      (tvb, pinfo, data_tree, offset);
				entry_tree = proto_item_add_subtree(
				    entry_item,
				    *lanman->ett_resp_data_element_item);
			} else {
				/*
				 * Just leave it at the current
				 * level.
				 */
				entry_item = NULL;
				entry_tree = data_tree;
			}

			offset = dissect_transact_data(tvb, offset,
			    convert, pinfo, entry_tree,
			    trp->data_descrip, resp_data, &aux_count);

			/* auxiliary data */
			if (trp->aux_data_descrip != NULL) {
				for (j = 0; j < aux_count; j++) {
					offset = dissect_transact_data(
					    tvb, offset, convert,
					    pinfo, entry_tree,
					    trp->data_descrip,
					    lanman->resp_aux_data, NULL);
				}
			}

			if (entry_item != NULL) {
				/*
				 * Set the length of the protocol tree
				 * item for the entry.
				 */
				proto_item_set_len(entry_item,
				    offset - start_offset);
			}
		}
	}

	if (data_item != NULL) {
		/*
		 * Set the length of the protocol tree item
		 * for the data.
		 */
		proto_item_set_len(data_item, offset);
	}
}

static gboolean
dissect_pipe_lanman(tvbuff_t *pd_tvb, tvbuff_t *p_tvb, tvbuff_t *d_tvb,
		    packet_info *pinfo, proto_tree *parent_tree)
{
	smb_info_t *smb_info = pinfo->private_data;
	smb_transact_info_t *trp = smb_info->sip->extra_info;
	int offset = 0, start_offset;
	guint16 cmd;
	guint16 status;
	int convert;
	const struct lanman_desc *lanman;
	proto_item *item = NULL;
	proto_tree *tree = NULL;
	guint descriptor_len;
	const gchar *param_descrip, *data_descrip, *aux_data_descrip = NULL;
	gboolean has_data;
	gboolean has_ent_count;
	guint16 ent_count, aux_count;
	guint i;
	proto_item *data_item;
	proto_tree *data_tree;

	if (!proto_is_protocol_enabled(proto_smb_lanman))
		return FALSE;
	if (smb_info->request && p_tvb == NULL) {
		/*
		 * Requests must have parameters.
		 */
		return FALSE;
	}
	pinfo->current_proto = "LANMAN";

	if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "LANMAN");
	}

	if (parent_tree) {
		item = proto_tree_add_item(parent_tree, proto_smb_lanman,
			pd_tvb, 0, -1, FALSE);
		tree = proto_item_add_subtree(item, ett_lanman);
	}

	if (smb_info->request) { /* this is a request */
		/* function code */
		cmd = tvb_get_letohs(p_tvb, offset);
		if (check_col(pinfo->cinfo, COL_INFO)) {
			col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", val_to_str(cmd, commands, "Unknown Command (%u)"));
		}
		proto_tree_add_uint(tree, hf_function_code, p_tvb, offset, 2,
		    cmd);
		offset += 2;

		/*
		 * If we haven't already done so, save the function code in
		 * the structure we were handed, so that it's available to
		 * the code parsing the reply, and initialize the detail
		 * level to -1, meaning "unknown".
		 */
		if (!pinfo->fd->flags.visited) {
			trp->lanman_cmd = cmd;
			trp->info_level = -1;
			trp->param_descrip=NULL;
			trp->data_descrip=NULL;
			trp->aux_data_descrip=NULL;
		}

		/* parameter descriptor */
		descriptor_len = tvb_strsize(p_tvb, offset);
		proto_tree_add_item(tree, hf_param_desc, p_tvb, offset,
		    descriptor_len, TRUE);
		param_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
		if (!pinfo->fd->flags.visited) {
			/*
			 * Save the parameter descriptor for future use.
			 */
			g_assert(trp->param_descrip == NULL);
			trp->param_descrip = g_strdup(param_descrip);
		}
		offset += descriptor_len;

		/* return descriptor */
		descriptor_len = tvb_strsize(p_tvb, offset);
		proto_tree_add_item(tree, hf_return_desc, p_tvb, offset,
		    descriptor_len, TRUE);
		data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
		if (!pinfo->fd->flags.visited) {
			/*
			 * Save the return descriptor for future use.
			 */
			g_assert(trp->data_descrip == NULL);
			trp->data_descrip = g_strdup(data_descrip);
		}
		offset += descriptor_len;

		lanman = find_lanman(cmd);

		/* request parameters */
		start_offset = offset;
		offset = dissect_request_parameters(p_tvb, offset, pinfo, tree,
		    param_descrip, lanman->req, &has_data);

		/* auxiliary data descriptor */
		if (tvb_reported_length_remaining(p_tvb, offset) > 0){
			/*
			 * There are more parameters left, so the next
			 * item is the auxiliary data descriptor.
			 */
			descriptor_len = tvb_strsize(p_tvb, offset);
			proto_tree_add_item(tree, hf_aux_data_desc, p_tvb, offset,
			    descriptor_len, TRUE);
			aux_data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
			if (!pinfo->fd->flags.visited) {
				/*
				 * Save the auxiliary data descriptor for
				 * future use.
				 */
				g_assert(trp->aux_data_descrip == NULL);
				trp->aux_data_descrip =
				    g_strdup(aux_data_descrip);
			}
			offset += descriptor_len;
		}

		/* reset offset, we now start dissecting the data area */
		offset = 0;
		if (has_data && d_tvb && tvb_reported_length(d_tvb) != 0) {
			/*
			 * There's a send buffer item in the descriptor
			 * string, and the data count in the transaction
			 * is non-zero, so there's data to dissect.
			 */

			if (lanman->req_data_item != NULL) {
				/*
				 * Create a protocol tree item for the data.
				 */
				data_item = (*lanman->req_data_item)(d_tvb,
				    pinfo, tree, offset);
				data_tree = proto_item_add_subtree(data_item,
				    *lanman->ett_req_data);
			} else {
				/*
				 * Just leave it at the top level.
				 */
				data_item = NULL;
				data_tree = tree;
			}

			/* data */
			offset = dissect_transact_data(d_tvb, offset, -1,
			    pinfo, data_tree, data_descrip, lanman->req_data,
			    &aux_count);	/* XXX - what about strings? */

			/* auxiliary data */
			if (aux_data_descrip != NULL) {
				for (i = 0; i < aux_count; i++) {
					offset = dissect_transact_data(d_tvb,
					    offset, -1, pinfo, data_tree,
					    aux_data_descrip,
					    lanman->req_aux_data, NULL);
				}
			}

			if (data_item != NULL) {
				/*
				 * Set the length of the protocol tree item
				 * for the data.
				 */
				proto_item_set_len(data_item, offset);
			}
		}
	} else {
		/*
		 * This is a response.
		 * Have we seen the request to which it's a response?
		 */
		if (trp == NULL)
			return FALSE;	/* no - can't dissect it */

		/* ok we have seen this one before */

		/* if it looks like an interim response, update COL_INFO and return */
		if( ( (p_tvb==NULL) || (tvb_reported_length(p_tvb)==0) )
		&&  ( (d_tvb==NULL) || (tvb_reported_length(d_tvb)==0) ) ){
			/* command */
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_add_fstr(pinfo->cinfo, COL_INFO, "%s Interim Response",
					     val_to_str(trp->lanman_cmd, commands, "Unknown Command (%u)"));
			}
			proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0, trp->lanman_cmd);
			return TRUE;
		}

		/* command */
		if (check_col(pinfo->cinfo, COL_INFO)) {
			col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response",
				     val_to_str(trp->lanman_cmd, commands, "Unknown Command (%u)"));
		}
		proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0,
		    trp->lanman_cmd);

		lanman = find_lanman(trp->lanman_cmd);

		/* response parameters */

		/* status */
		status = tvb_get_letohs(p_tvb, offset);
		proto_tree_add_uint(tree, hf_status, p_tvb, offset, 2, status);
		offset += 2;

		/* convert */
		convert = tvb_get_letohs(p_tvb, offset);
		proto_tree_add_uint(tree, hf_convert, p_tvb, offset, 2, convert);
		offset += 2;

		/* rest of the parameters */
		offset = dissect_response_parameters(p_tvb, offset, pinfo, tree,
		    trp->param_descrip, lanman->resp, &has_data,
		    &has_ent_count, &ent_count);

		/* reset offset, we now start dissecting the data area */
		offset = 0;
		/* data */
		if (has_data && d_tvb && tvb_reported_length(d_tvb) > 0) {
			/*
			 * There's a receive buffer item in the descriptor
			 * string, and the data count in the transaction
			 * is non-zero, so there's data to dissect.
			 */
			dissect_response_data(d_tvb, pinfo, convert, tree,
			    smb_info, lanman, has_ent_count, ent_count);
		}
	}

	return TRUE;
}

void
proto_register_pipe_lanman(void)
{
	static hf_register_info hf[] = {
		{ &hf_function_code,
			{ "Function Code", "lanman.function_code", FT_UINT16, BASE_DEC,
			VALS(commands), 0, "LANMAN Function Code/Command", HFILL }},

		{ &hf_param_desc,
			{ "Parameter Descriptor", "lanman.param_desc", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Parameter Descriptor", HFILL }},

		{ &hf_return_desc,
			{ "Return Descriptor", "lanman.ret_desc", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Return Descriptor", HFILL }},

		{ &hf_aux_data_desc,
			{ "Auxiliary Data Descriptor", "lanman.aux_data_desc", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Auxiliary Data Descriptor", HFILL }},

		{ &hf_detail_level,
			{ "Detail Level", "lanman.level", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Detail Level", HFILL }},

		{ &hf_recv_buf_len,
			{ "Receive Buffer Length", "lanman.recv_buf_len", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Receive Buffer Length", HFILL }},

		{ &hf_send_buf_len,
			{ "Send Buffer Length", "lanman.send_buf_len", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Send Buffer Length", HFILL }},

		{ &hf_continuation_from,
			{ "Continuation from message in frame", "lanman.continuation_from", FT_UINT32, BASE_DEC,
			NULL, 0, "This is a LANMAN continuation from the message in the frame in question", HFILL }},

		{ &hf_status,
			{ "Status", "lanman.status", FT_UINT16, BASE_DEC,
			VALS(status_vals), 0, "LANMAN Return status", HFILL }},

		{ &hf_convert,
			{ "Convert", "lanman.convert", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Convert", HFILL }},

		{ &hf_ecount,
			{ "Entry Count", "lanman.entry_count", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Number of Entries", HFILL }},

		{ &hf_acount,
			{ "Available Entries", "lanman.available_count", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Number of Available Entries", HFILL }},

		{ &hf_share_name,
			{ "Share Name", "lanman.share.name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Name of Share", HFILL }},

		{ &hf_share_type,
			{ "Share Type", "lanman.share.type", FT_UINT16, BASE_DEC,
			VALS(share_type_vals), 0, "LANMAN Type of Share", HFILL }},

		{ &hf_share_comment,
			{ "Share Comment", "lanman.share.comment", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Share Comment", HFILL }},

		{ &hf_share_permissions,
			{ "Share Permissions", "lanman.share.permissions", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Permissions on share", HFILL }},

		{ &hf_share_max_uses,
			{ "Share Max Uses", "lanman.share.max_uses", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Max connections allowed to share", HFILL }},

		{ &hf_share_current_uses,
			{ "Share Current Uses", "lanman.share.current_uses", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Current connections to share", HFILL }},

		{ &hf_share_path,
			{ "Share Path", "lanman.share.path", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Share Path", HFILL }},

		{ &hf_share_password,
			{ "Share Password", "lanman.share.password", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Share Password", HFILL }},

		{ &hf_server_name,
			{ "Server Name", "lanman.server.name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Name of Server", HFILL }},

		{ &hf_server_major,
			{ "Major Version", "lanman.server.major", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Server Major Version", HFILL }},

		{ &hf_server_minor,
			{ "Minor Version", "lanman.server.minor", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Server Minor Version", HFILL }},

		{ &hf_server_comment,
			{ "Server Comment", "lanman.server.comment", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Server Comment", HFILL }},

		{ &hf_abytes,
			{ "Available Bytes", "lanman.available_bytes", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Number of Available Bytes", HFILL }},

		{ &hf_current_time,
			{ "Current Date/Time", "lanman.current_time", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Current date and time, in seconds since 00:00:00, January 1, 1970", HFILL }},

		{ &hf_msecs,
			{ "Milliseconds", "lanman.msecs", FT_UINT32, BASE_DEC,
			NULL, 0, "LANMAN Milliseconds since arbitrary time in the past (typically boot time)", HFILL }},

		{ &hf_hour,
			{ "Hour", "lanman.hour", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current hour", HFILL }},

		{ &hf_minute,
			{ "Minute", "lanman.minute", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current minute", HFILL }},

		{ &hf_second,
			{ "Second", "lanman.second", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current second", HFILL }},

		{ &hf_hundredths,
			{ "Hundredths of a second", "lanman.hundredths", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current hundredths of a second", HFILL }},

		{ &hf_tzoffset,
			{ "Time Zone Offset", "lanman.tzoffset", FT_INT16, BASE_DEC,
			NULL, 0, "LANMAN Offset of time zone from GMT, in minutes", HFILL }},

		{ &hf_timeinterval,
			{ "Time Interval", "lanman.timeinterval", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN .0001 second units per clock tick", HFILL }},

		{ &hf_day,
			{ "Day", "lanman.day", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current day", HFILL }},

		{ &hf_month,
			{ "Month", "lanman.month", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Current month", HFILL }},

		{ &hf_year,
			{ "Year", "lanman.year", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Current year", HFILL }},

		{ &hf_weekday,
			{ "Weekday", "lanman.weekday", FT_UINT8, BASE_DEC,
			VALS(weekday_vals), 0, "LANMAN Current day of the week", HFILL }},

		{ &hf_enumeration_domain,
			{ "Enumeration Domain", "lanman.enumeration_domain", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Domain in which to enumerate servers", HFILL }},

		{ &hf_computer_name,
			{ "Computer Name", "lanman.computer_name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Computer Name", HFILL }},

		{ &hf_user_name,
			{ "User Name", "lanman.user_name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN User Name", HFILL }},

		{ &hf_group_name,
			{ "Group Name", "lanman.group_name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Group Name", HFILL }},

		{ &hf_workstation_domain,
			{ "Workstation Domain", "lanman.workstation_domain", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Workstation Domain", HFILL }},

		{ &hf_workstation_major,
			{ "Workstation Major Version", "lanman.workstation_major", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Workstation Major Version", HFILL }},

		{ &hf_workstation_minor,
			{ "Workstation Minor Version", "lanman.workstation_minor", FT_UINT8, BASE_DEC,
			NULL, 0, "LANMAN Workstation Minor Version", HFILL }},

		{ &hf_logon_domain,
			{ "Logon Domain", "lanman.logon_domain", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Logon Domain", HFILL }},

		{ &hf_other_domains,
			{ "Other Domains", "lanman.other_domains", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Other Domains", HFILL }},

		{ &hf_password,
			{ "Password", "lanman.password", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Password", HFILL }},

		{ &hf_workstation_name,
			{ "Workstation Name", "lanman.workstation_name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Workstation Name", HFILL }},

		{ &hf_ustruct_size,
			{ "Length of UStruct", "lanman.ustruct_size", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN UStruct Length", HFILL }},

		{ &hf_logon_code,
			{ "Logon Code", "lanman.logon_code", FT_UINT16, BASE_DEC,
			VALS(status_vals), 0, "LANMAN Logon Code", HFILL }},

		{ &hf_privilege_level,
			{ "Privilege Level", "lanman.privilege_level", FT_UINT16, BASE_DEC,
			VALS(privilege_vals), 0, "LANMAN Privilege Level", HFILL }},

		{ &hf_operator_privileges,
			{ "Operator Privileges", "lanman.operator_privileges", FT_UINT32, BASE_DEC,
			VALS(op_privilege_vals), 0, "LANMAN Operator Privileges", HFILL }},

		{ &hf_num_logons,
			{ "Number of Logons", "lanman.num_logons", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Number of Logons", HFILL }},

		{ &hf_bad_pw_count,
			{ "Bad Password Count", "lanman.bad_pw_count", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Number of incorrect passwords entered since last successful login", HFILL }},

		{ &hf_last_logon,
			{ "Last Logon Date/Time", "lanman.last_logon", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time of last logon", HFILL }},

		{ &hf_last_logoff,
			{ "Last Logoff Date/Time", "lanman.last_logoff", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time of last logoff", HFILL }},

		{ &hf_logoff_time,
			{ "Logoff Date/Time", "lanman.logoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time when user should log off", HFILL }},

		{ &hf_kickoff_time,
			{ "Kickoff Date/Time", "lanman.kickoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time when user will be logged off", HFILL }},

		{ &hf_password_age,
			{ "Password Age", "lanman.password_age", FT_RELATIVE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Time since user last changed his/her password", HFILL }},

		{ &hf_password_can_change,
			{ "Password Can Change", "lanman.password_can_change", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time when user can change their password", HFILL }},

		{ &hf_password_must_change,
			{ "Password Must Change", "lanman.password_must_change", FT_ABSOLUTE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Date and time when user must change their password", HFILL }},

		{ &hf_script_path,
			{ "Script Path", "lanman.script_path", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Pathname of user's logon script", HFILL }},

		{ &hf_logoff_code,
			{ "Logoff Code", "lanman.logoff_code", FT_UINT16, BASE_DEC,
			VALS(status_vals), 0, "LANMAN Logoff Code", HFILL }},

		{ &hf_duration,
			{ "Duration of Session", "lanman.duration", FT_RELATIVE_TIME, BASE_NONE,
			NULL, 0, "LANMAN Number of seconds the user was logged on", HFILL }},

		{ &hf_comment,
			{ "Comment", "lanman.comment", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Comment", HFILL }},

		{ &hf_user_comment,
			{ "User Comment", "lanman.user_comment", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN User Comment", HFILL }},

		{ &hf_full_name,
			{ "Full Name", "lanman.full_name", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Full Name", HFILL }},

		{ &hf_homedir,
			{ "Home Directory", "lanman.homedir", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Home Directory", HFILL }},

		{ &hf_parameters,
			{ "Parameters", "lanman.parameters", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Parameters", HFILL }},

		{ &hf_logon_server,
			{ "Logon Server", "lanman.logon_server", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Logon Server", HFILL }},

		/* XXX - we should have a value_string table for this */
		{ &hf_country_code,
			{ "Country Code", "lanman.country_code", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Country Code", HFILL }},

		{ &hf_workstations,
			{ "Workstations", "lanman.workstations", FT_STRING, BASE_NONE,
			NULL, 0, "LANMAN Workstations", HFILL }},

		{ &hf_max_storage,
			{ "Max Storage", "lanman.max_storage", FT_UINT32, BASE_DEC,
			NULL, 0, "LANMAN Max Storage", HFILL }},

		{ &hf_units_per_week,
			{ "Units Per Week", "lanman.units_per_week", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Units Per Week", HFILL }},

		{ &hf_logon_hours,
			{ "Logon Hours", "lanman.logon_hours", FT_BYTES, BASE_NONE,
			NULL, 0, "LANMAN Logon Hours", HFILL }},

		/* XXX - we should have a value_string table for this */
		{ &hf_code_page,
			{ "Code Page", "lanman.code_page", FT_UINT16, BASE_DEC,
			NULL, 0, "LANMAN Code Page", HFILL }},

		{ &hf_new_password,
			{ "New Password", "lanman.new_password", FT_BYTES, BASE_HEX,
			NULL, 0, "LANMAN New Password (encrypted)", HFILL }},

		{ &hf_old_password,
			{ "Old Password", "lanman.old_password", FT_BYTES, BASE_HEX,
			NULL, 0, "LANMAN Old Password (encrypted)", HFILL }},

		{ &hf_reserved,
			{ "Reserved", "lanman.reserved", FT_UINT32, BASE_HEX,
			NULL, 0, "LANMAN Reserved", HFILL }},

	};
	static gint *ett[] = {
		&ett_lanman,
		&ett_lanman_unknown_entries,
		&ett_lanman_unknown_entry,
		&ett_lanman_servers,
		&ett_lanman_server,
		&ett_lanman_groups,
		&ett_lanman_shares,
		&ett_lanman_share,
	};

	proto_smb_lanman = proto_register_protocol(
		"Microsoft Windows Lanman Remote API Protocol", "LANMAN", "lanman");
	proto_register_field_array(proto_smb_lanman, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
}

static heur_dissector_list_t smb_transact_heur_subdissector_list;

static gboolean
dissect_pipe_dcerpc(tvbuff_t *d_tvb, packet_info *pinfo, proto_tree *parent_tree,
    proto_tree *tree, guint32 fid)
{
	dcerpc_private_info dcerpc_priv;
	smb_info_t *smb_priv = (smb_info_t *)pinfo->private_data;
	gboolean result;
	gboolean save_fragmented;

	dcerpc_priv.transport_type = DCERPC_TRANSPORT_SMB;
	dcerpc_priv.data.smb.fid = fid;

	pinfo->private_data = &dcerpc_priv;

	/* offer desegmentation service to DCERPC */
	pinfo->can_desegment=0;
	pinfo->desegment_offset = 0;
	pinfo->desegment_len = 0;
	if(smb_dcerpc_reassembly){ 
		pinfo->can_desegment=2;
	}

	save_fragmented = pinfo->fragmented;

	/* see if this packet is already desegmented */
	if(smb_dcerpc_reassembly && pinfo->fd->flags.visited){
		fragment_data *fd_head;
		tvbuff_t *new_tvb;

		fd_head=fragment_get(pinfo, pinfo->fd->num , 
			dcerpc_fragment_table);
		if(fd_head && fd_head->flags&FD_DEFRAGMENTED){
			proto_tree *tr;
			proto_item *it;
			fragment_data *fd;
		
			new_tvb = tvb_new_real_data(fd_head->data,
				  fd_head->datalen, fd_head->datalen,
				  "DCERPC over SMB");
			tvb_set_child_real_data_tvbuff(d_tvb, new_tvb);
			pinfo->fd->data_src=g_slist_append(pinfo->fd->data_src,
				   new_tvb);
			pinfo->fragmented=FALSE;

			d_tvb=new_tvb;

			/* list what segments we have */
			it = proto_tree_add_text(tree, d_tvb, 0, 0, "Fragments");
			tr = proto_item_add_subtree(it, ett_smb_pipe_fragments);
			for(fd=fd_head->next;fd;fd=fd->next){
				proto_tree_add_text(tr, d_tvb, 0, 0, "Frame:%u Data:%u-%u",
					    fd->frame, fd->offset, fd->offset+fd->len-1);
			}
		}
	}

	result = dissector_try_heuristic(smb_transact_heur_subdissector_list, d_tvb,
					 pinfo, parent_tree);
	pinfo->private_data = smb_priv;

	/* check if dissector wanted us to desegment the data */
	if(smb_dcerpc_reassembly && !pinfo->fd->flags.visited && pinfo->desegment_len){
		fragment_add(d_tvb, 0, pinfo, pinfo->fd->num,
			dcerpc_fragment_table,
			0, tvb_length(d_tvb), TRUE);
		fragment_set_tot_len(pinfo, pinfo->fd->num, 
			dcerpc_fragment_table, 
			pinfo->desegment_len+tvb_length(d_tvb));
		/* since the other fragments are in normal ReadAndX and WriteAndX calls
		   we must make sure we can map FID values to this defragmentation
		   session */
		/* first remove any old mappings */
		if(g_hash_table_lookup(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid)){
			g_hash_table_remove(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid);
		}
		g_hash_table_insert(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid, 
			(void *)pinfo->fd->num);
	}
	/* clear out the variables */
	pinfo->can_desegment=0;
	pinfo->desegment_offset = 0;
	pinfo->desegment_len = 0;

	if (!result)
		call_dissector(data_handle, d_tvb, pinfo, parent_tree);

	pinfo->fragmented = save_fragmented;
	return TRUE;
}

void
proto_register_pipe_dcerpc(void)
{
	register_heur_dissector_list("smb_transact", &smb_transact_heur_subdissector_list);
}

#define CALL_NAMED_PIPE		0x54
#define WAIT_NAMED_PIPE		0x53
#define PEEK_NAMED_PIPE		0x23
#define Q_NM_P_HAND_STATE	0x21
#define SET_NM_P_HAND_STATE	0x01
#define Q_NM_PIPE_INFO		0x22
#define TRANSACT_NM_PIPE	0x26
#define RAW_READ_NM_PIPE	0x11
#define RAW_WRITE_NM_PIPE	0x31

static const value_string functions[] = {
	{CALL_NAMED_PIPE,	"CallNamedPipe"},
	{WAIT_NAMED_PIPE,	"WaitNamedPipe"},
	{PEEK_NAMED_PIPE,	"PeekNamedPipe"},
	{Q_NM_P_HAND_STATE,	"QNmPHandState"},
	{SET_NM_P_HAND_STATE,	"SetNmPHandState"},
	{Q_NM_PIPE_INFO,	"QNmPipeInfo"},
	{TRANSACT_NM_PIPE,	"TransactNmPipe"},
	{RAW_READ_NM_PIPE,	"RawReadNmPipe"},
	{RAW_WRITE_NM_PIPE,	"RawWriteNmPipe"},
	{0,			NULL}
};

static const value_string pipe_status[] = {
	{1,	"Disconnected by server"},
	{2,	"Listening"},
	{3,	"Connection to server is OK"},
	{4,	"Server end of pipe is closed"},
	{0,	NULL}
};

#define PIPE_LANMAN     1
#define PIPE_DCERPC     2

/* decode the SMB pipe protocol
   for requests
    pipe is the name of the pipe, e.g. LANMAN
    smb_info->trans_subcmd is set to the symbolic constant matching the mailslot name
  for responses
    pipe is NULL
    smb_info->trans_subcmd gives us which pipe this response is for
*/
gboolean
dissect_pipe_smb(tvbuff_t *sp_tvb, tvbuff_t *s_tvb, tvbuff_t *pd_tvb,
		 tvbuff_t *p_tvb, tvbuff_t *d_tvb, const char *pipe,
		 packet_info *pinfo, proto_tree *tree)
{
	smb_info_t *smb_info;
	smb_transact_info_t *tri;
	guint sp_len;
	proto_item *pipe_item = NULL;
	proto_tree *pipe_tree = NULL;
	int offset;
	int trans_subcmd;
	int function;
	int fid = -1;
	guint16 info_level;

	if (!proto_is_protocol_enabled(proto_smb_pipe))
		return FALSE;
	pinfo->current_proto = "SMB Pipe";

	smb_info = pinfo->private_data;

	/*
	 * Set the columns.
	 */
	if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMB Pipe");
	}
	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_set_str(pinfo->cinfo, COL_INFO,
		    smb_info->request ? "Request" : "Response");
	}

	if (smb_info->sip != NULL)
		tri = smb_info->sip->extra_info;
	else
		tri = NULL;

	/*
	 * Set up a subtree for the pipe protocol.  (It might not contain
	 * anything.)
	 */
	if (sp_tvb != NULL)
		sp_len = tvb_length(sp_tvb);
	else
		sp_len = 0;
	if (tree) {
		pipe_item = proto_tree_add_item(tree, proto_smb_pipe,
		    sp_tvb, 0, sp_len, FALSE);
		pipe_tree = proto_item_add_subtree(pipe_item, ett_smb_pipe);
	}
	offset = 0;

	/*
	 * Do we have any setup words at all?
	 */
	if (s_tvb != NULL && tvb_length(s_tvb) != 0) {
		/*
		 * Yes.  The first of them is the function.
		 */
		function = tvb_get_letohs(s_tvb, offset);
		proto_tree_add_uint(pipe_tree, hf_pipe_function, s_tvb,
		    offset, 2, function);
		offset += 2;
		if (check_col(pinfo->cinfo, COL_INFO)) {
			col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
			    val_to_str(function, functions, "Unknown function (0x%04x)"),
			    smb_info->request ? "Request" : "Response");
		}
		if (tri != NULL)
			tri->function = function;

		/*
		 * The second of them depends on the function.
		 */
		switch (function) {

		case CALL_NAMED_PIPE:
		case WAIT_NAMED_PIPE:
			/*
			 * It's a priority.
			 */
			proto_tree_add_item(pipe_tree, hf_pipe_priority, s_tvb,
			    offset, 2, TRUE);
			break;

		case PEEK_NAMED_PIPE:
		case Q_NM_P_HAND_STATE:
		case SET_NM_P_HAND_STATE:
		case Q_NM_PIPE_INFO:
		case TRANSACT_NM_PIPE:
		case RAW_READ_NM_PIPE:
		case RAW_WRITE_NM_PIPE:
			/*
			 * It's a FID.
			 */
			fid = tvb_get_letohs(s_tvb, 2);
			add_fid(s_tvb, pinfo, pipe_tree, offset, 2, fid);
			if (tri != NULL)
				tri->fid = fid;
			break;

		default:
			/*
			 * It's something unknown.
			 * XXX - put it into the tree?
			 */
			break;
		}
		offset += 2;
	} else {
		/*
		 * This is either a response or a pipe transaction with
		 * no setup information.
		 *
		 * In the former case, we can get that information from
		 * the matching request, if we saw it.
		 *
		 * In the latter case, there is no function or FID.
		 */
		if (tri != NULL && tri->function != -1) {
			function = tri->function;
			proto_tree_add_uint(pipe_tree, hf_pipe_function, NULL,
			    0, 0, function);
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
				    val_to_str(function, functions, "Unknown function (0x%04x)"),
				    smb_info->request ? "Request" : "Response");
			}
			fid = tri->fid;
			if (fid != -1)
				add_fid(NULL, pinfo, pipe_tree, 0, 0, fid);
		} else {
			function = -1;
			fid = -1;
		}
	}

	/*
	 * XXX - put the byte count and the pipe name into the tree as well;
	 * that requires us to fetch a possibly-Unicode string.
	 */

	if(smb_info->request){
		if(strncmp(pipe,"LANMAN",6) == 0){
			trans_subcmd=PIPE_LANMAN;
		} else {
			/* assume it is DCERPC */
			trans_subcmd=PIPE_DCERPC;
		}
		
		if (!pinfo->fd->flags.visited)
			tri->trans_subcmd = trans_subcmd;
	} else
		trans_subcmd = tri->trans_subcmd;

	if (tri == NULL) {
		/*
		 * We don't know what type of pipe transaction this
		 * was, so indicate that we didn't dissect it.
		 */
		return FALSE;
	}

	switch (function) {

	case CALL_NAMED_PIPE:
	case TRANSACT_NM_PIPE:
		switch(trans_subcmd){

		case PIPE_LANMAN:
			return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
			    tree);
			break;

		case PIPE_DCERPC:
			/*
			 * Only dissect this if we know the FID.
			 */
			if (fid != -1) {
				if (d_tvb == NULL)
					return FALSE;
		                return dissect_pipe_dcerpc(d_tvb, pinfo, tree,
		                    pipe_tree, fid);
		        }
			break;
		}
		break;

	case -1:
		/*
		 * We don't know the function; we dissect only LANMAN
		 * pipe messages, not RPC pipe messages, in that case.
		 */
		switch(trans_subcmd){
		case PIPE_LANMAN:
			return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
			    tree);
			break;
		}
		break;

	case WAIT_NAMED_PIPE:
		break;

	case PEEK_NAMED_PIPE:
		/*
		 * Request contains no parameters or data.
		 */
		if (!smb_info->request) {
			if (p_tvb == NULL)
				return FALSE;
			offset = 0;
			proto_tree_add_item(pipe_tree, hf_pipe_peek_available,
			    p_tvb, offset, 2, TRUE);
			offset += 2;
			proto_tree_add_item(pipe_tree, hf_pipe_peek_remaining,
			    p_tvb, offset, 2, TRUE);
			offset += 2;
			proto_tree_add_item(pipe_tree, hf_pipe_peek_status,
			    p_tvb, offset, 2, TRUE);
			offset += 2;
		}
		break;

	case Q_NM_P_HAND_STATE:
		/*
		 * Request contains no parameters or data.
		 */
		if (!smb_info->request) {
			if (p_tvb == NULL)
				return FALSE;
			offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
			    FALSE);
		}
		break;

	case SET_NM_P_HAND_STATE:
		/*
		 * Response contains no parameters or data.
		 */
		if (smb_info->request) {
			if (p_tvb == NULL)
				return FALSE;
			offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
			    TRUE);
		}
		break;

	case Q_NM_PIPE_INFO:
		offset = 0;
		if (smb_info->request) {
			if (p_tvb == NULL)
				return FALSE;

			/*
			 * Request contains an information level.
			 */
			info_level = tvb_get_letohs(p_tvb, offset);
			proto_tree_add_uint(pipe_tree, hf_pipe_getinfo_info_level,
			    p_tvb, offset, 2, info_level);
			offset += 2;
			if (!pinfo->fd->flags.visited)
				tri->info_level = info_level;
		} else {
			guint8 pipe_namelen;

			if (d_tvb == NULL)
				return FALSE;

			switch (tri->info_level) {

			case 1:
				proto_tree_add_item(pipe_tree,
				    hf_pipe_getinfo_output_buffer_size,
				    d_tvb, offset, 2, TRUE);
				offset += 2;
				proto_tree_add_item(pipe_tree,
				    hf_pipe_getinfo_input_buffer_size,
				    d_tvb, offset, 2, TRUE);
				offset += 2;
				proto_tree_add_item(pipe_tree,
				    hf_pipe_getinfo_maximum_instances,
				    d_tvb, offset, 1, TRUE);
				offset += 1;
				proto_tree_add_item(pipe_tree,
				    hf_pipe_getinfo_current_instances,
				    d_tvb, offset, 1, TRUE);
				offset += 1;
				pipe_namelen = tvb_get_guint8(d_tvb, offset);
				proto_tree_add_uint(pipe_tree,
				    hf_pipe_getinfo_pipe_name_length,
				    d_tvb, offset, 1, pipe_namelen);
				offset += 1;
				/* XXX - can this be Unicode? */
				proto_tree_add_item(pipe_tree,
				    hf_pipe_getinfo_pipe_name,
				    d_tvb, offset, pipe_namelen, TRUE);
				break;
			}
		}
		break;

	case RAW_READ_NM_PIPE:
		/*
		 * Request contains no parameters or data.
		 */
		if (!smb_info->request) {
			if (d_tvb == NULL)
				return FALSE;

			offset = dissect_file_data(d_tvb, pinfo, pipe_tree, 0,
			    tvb_reported_length(d_tvb),
			    tvb_reported_length(d_tvb));
		}
		break;

	case RAW_WRITE_NM_PIPE:
		offset = 0;
		if (smb_info->request) {
			if (d_tvb == NULL)
				return FALSE;

			offset = dissect_file_data(d_tvb, pinfo, pipe_tree,
			    offset, tvb_reported_length(d_tvb),
			    tvb_reported_length(d_tvb));
		} else {
			if (p_tvb == NULL)
				return FALSE;
			proto_tree_add_item(pipe_tree,
			    hf_pipe_write_raw_bytes_written,
			    p_tvb, offset, 2, TRUE);
			offset += 2;
		}
		break;
	}
	return TRUE;
}

void
proto_register_smb_pipe(void)
{
	static hf_register_info hf[] = {
		{ &hf_pipe_function,
			{ "Function", "pipe.function", FT_UINT16, BASE_HEX,
			VALS(functions), 0, "SMB Pipe Function Code", HFILL }},
		{ &hf_pipe_priority,
			{ "Priority", "pipe.priority", FT_UINT16, BASE_DEC,
			NULL, 0, "SMB Pipe Priority", HFILL }},
		{ &hf_pipe_peek_available,
			{ "Available Bytes", "pipe.peek.available_bytes", FT_UINT16, BASE_DEC,
			NULL, 0, "Total number of bytes available to be read from the pipe", HFILL }},
		{ &hf_pipe_peek_remaining,
			{ "Bytes Remaining", "pipe.peek.remaining_bytes", FT_UINT16, BASE_DEC,
			NULL, 0, "Total number of bytes remaining in the message at the head of the pipe", HFILL }},
		{ &hf_pipe_peek_status,
			{ "Pipe Status", "pipe.peek.status", FT_UINT16, BASE_DEC,
			VALS(pipe_status), 0, "Pipe status", HFILL }},
		{ &hf_pipe_getinfo_info_level,
			{ "Information Level", "pipe.getinfo.info_level", FT_UINT16, BASE_DEC,
			NULL, 0, "Information level of information to return", HFILL }},
		{ &hf_pipe_getinfo_output_buffer_size,
			{ "Output Buffer Size", "pipe.getinfo.output_buffer_size", FT_UINT16, BASE_DEC,
			NULL, 0, "Actual size of buffer for outgoing (server) I/O", HFILL }},
		{ &hf_pipe_getinfo_input_buffer_size,
			{ "Input Buffer Size", "pipe.getinfo.input_buffer_size", FT_UINT16, BASE_DEC,
			NULL, 0, "Actual size of buffer for incoming (client) I/O", HFILL }},
		{ &hf_pipe_getinfo_maximum_instances,
			{ "Maximum Instances", "pipe.getinfo.maximum_instances", FT_UINT8, BASE_DEC,
			NULL, 0, "Maximum allowed number of instances", HFILL }},
		{ &hf_pipe_getinfo_current_instances,
			{ "Current Instances", "pipe.getinfo.current_instances", FT_UINT8, BASE_DEC,
			NULL, 0, "Current number of instances", HFILL }},
		{ &hf_pipe_getinfo_pipe_name_length,
			{ "Pipe Name Length", "pipe.getinfo.pipe_name_length", FT_UINT8, BASE_DEC,
			NULL, 0, "Length of pipe name", HFILL }},
		{ &hf_pipe_getinfo_pipe_name,
			{ "Pipe Name", "pipe.getinfo.pipe_name", FT_STRING, BASE_NONE,
			NULL, 0, "Name of pipe", HFILL }},
		{ &hf_pipe_write_raw_bytes_written,
			{ "Bytes Written", "pipe.write_raw.bytes_written", FT_UINT16, BASE_DEC,
			NULL, 0, "Number of bytes written to the pipe", HFILL }},
	};
	static gint *ett[] = {
		&ett_smb_pipe,
		&ett_smb_pipe_fragments,
	};

	proto_smb_pipe = proto_register_protocol(
		"SMB Pipe Protocol", "SMB Pipe", "pipe");

	proto_register_field_array(proto_smb_pipe, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_smb_pipe(void)
{
	data_handle = find_dissector("data");
}
